0% encontró este documento útil (0 votos)
24 vistas667 páginas

Python Institute 1 y 2

El curso Fundamentos de Python 1 es el primero de dos que prepara a los estudiantes para las certificaciones PCEP y PCAP, enseñando desde los conceptos básicos de programación hasta la escritura y depuración de programas en Python. A lo largo de cuatro módulos, los participantes aprenderán sobre la sintaxis de Python, tipos de datos, estructuras de control, y funciones, además de la importancia de Python en diversas aplicaciones y su facilidad de aprendizaje. Al finalizar, los estudiantes estarán listos para obtener la certificación PCEP, que valida su conocimiento en programación y abre oportunidades en el desarrollo de software.

Cargado por

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

Python Institute 1 y 2

El curso Fundamentos de Python 1 es el primero de dos que prepara a los estudiantes para las certificaciones PCEP y PCAP, enseñando desde los conceptos básicos de programación hasta la escritura y depuración de programas en Python. A lo largo de cuatro módulos, los participantes aprenderán sobre la sintaxis de Python, tipos de datos, estructuras de control, y funciones, además de la importancia de Python en diversas aplicaciones y su facilidad de aprendizaje. Al finalizar, los estudiantes estarán listos para obtener la certificación PCEP, que valida su conocimiento en programación y abre oportunidades en el desarrollo de software.

Cargado por

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

Acerca de FP1

Bienvenido a Fundamentos de Python 1


Este curso es el primero en una serie de 2 cursos que te prepararán para los exámenes
de certificación PCEP - Certified Entry-Level Python Programmer y PCAP: Certified
Associate in Python Programming.

El objetivo principal del curso es guiarte desde un estado de analfabetismo completo en


programación a un nivel de conocimiento de programación que te
permita diseñar, escribir, depurar y ejecutar programas codificados en el lenguaje
Python, y comprender los conceptos básicos de la tecnología del desarrollo de software.

El curso te preparará para trabajos y carreras relacionadas con el ampliamente


conocido desarrollo de software, que incluye no solo la creación de código en sí como
desarrollador junior, sino también el diseño de sistemas y pruebas de software.

Temario | ¿Por qué aprender Python?

Temario del Curso


En este curso aprenderás:

• Los conceptos universales de la programación en computadora.


• La sintaxis y la semántica del lenguaje Python.
• Habilidades prácticas para resolver desafíos típicos de implementación.
• A utilizar los elementos más importantes de la biblioteca estándar de Python.
• A instalar tu entorno de ejecución.
• A diseñar, escribir, probar y depurar tus propios programas Python.

El curso se divide en 4 módulos:

1. Módulo 1
Introducción a Python y a la programación de computadora.
2. Módulo 2
Tipos de datos, variables, operaciones básicas de entrada y salida, y operadores
básicos.
3. Módulo 3
Valores booleanos, ejecución condicional, bucles, listas y su procesamiento,
operaciones lógicas y de bit a bit.
4. Módulo 4
Funciones, tuplas, diccionarios y procesamiento de datos.
¿Por qué deberías aprender PYTHON?
Python es omnipresente y muchas personas usan numerosos dispositivos con tecnología
de Python a diario, ya sea que se den cuenta o no. Ha habido millones (bueno, en
realidad miles de millones) de líneas de código escritas en Python, lo que significa
oportunidades casi ilimitadas para la reutilización de código y el aprendizaje de ejemplos
bien elaborados. Además, existe una comunidad de Python grande y muy activa, siempre
feliz de ayudar.

También hay un par de factores que hacen que Python sea excelente para el aprendizaje:

• Es fácil de aprender: el tiempo necesario para aprender Python es más corto que
para muchos otros lenguajes; esto significa que es posible iniciar la programación
real más rápido.
• Es fácil de usar para escribir software nuevo; a menudo es posible escribir código
más rápido cuando se usa Python.
• Es fácil de obtener, instalar e implementar: Python es gratuito, abierto y
multiplataforma; no todos los lenguajes pueden presumir de eso.

Completar este curso puede ser un trampolín para aprender cualquier otro lenguaje de
programación y para explorar tecnologías que usan a Python como base (por ejemplo,
Django). Este curso se distingue por su asequibilidad, amigabilidad y apertura para el
estudiante.

La primera parte comienza desde lo básico, guiándote paso a paso hacia los problemas
complejos que se explican en la parte 2, convirtiéndote en un creador de software
responsable capaz de asumir diferentes desafíos en muchas posiciones en la industria de
TI.

Aplicaciones para Python

¿Dónde se utiliza Python?


¿Te acuerdas de Battlefield 2, Battlefield 2142 y Battlefield Heroes? Son juegos de
estrategia y disparos en primera persona de EA DICE. Todos estos juegos usan Python
para la lógica y los controles del servidor. Python se usa con frecuencia para crear juegos
gratuitos de código abierto, por ejemplo, OpenRTS, PySol, Metin 2 o Frets On Fire, juegos
famosos tipo Guitar Hero fueron escritos en pygame.

¿Y los principales sitios web y servicios como Dropbox, UBER, Spotify, Pintrest y
BuzzFeed? Si. Todos fueron escritos, en mayor o menor medida, en Python. ¿Otros
ejemplos?

• Aplicaciones de Internet (BitTorrent, Jogger Publishing Assistant, TheCircle,


TwistedMatrix)
• 3D CAD/CAM (FreeCAD, Fandango, Blender, Vintech RCAM)
• Aplicaciones Empresariales (Odoo, Tryton, Picalo, LinOTP 2, RESTx)
• Aplicaciones de Imagen (Gnofract 4D, Gogh, imgSeek, MayaVi, VPython)
• Aplicaciones Móviles (Aarlogic C05/3, AppBackup, Pyroute)
• Aplicaciones de Oficina (calibre, faces, Notalon, pyspread)
• Administradores de Información Personal (BitPim, Narval, Prioritise, Task Coach,
WikidPad)
(Fuente: https://wiki.python.org/moin/PythonProjects)

Generalmente, Python es una excelente opción para:

• Desarrollo Web (por ejemplo, los frameworks Django y Pyramid, micro-


frameworks Flask y Bottle)
• Computación científica y numérica (por ejemplo, SciPy, una colección de paquetes
con fines matemáticos, científicos y de ingeniería; Ipython, un shell interactivo que
permite la edición y grabación de sesiones de trabajo)
• Educación (¡es un lenguaje brillante para enseñar programación! ¡Y es por eso que
te ofrecemos este curso!)
• GUIs de Escritorio (por ejemplo, wxWidgets, Kivy, Qt)
• Desarrollo de software (control de compilación, gestión y pruebas: Scons,
Buildbot, Apache Gump, Roundup, Trac)
• Aplicaciones empresariales (ERP y sistemas de comercio electrónico: Odoo,
Tryton)
(Fuente: https://www.python.org/about/apps)

Y muchos otros proyectos y herramientas de desarrollo.

Obtén la certificación PCEP


Certifícate en PCEP
Al finalizar Fundamentos de Python Parte 1, estarás preparado para intentar obtener la
certificación PCEP – Certified Entry-Level Python Programmer.

La certificación PCEP – Certified Entry-Level Python Programmer demuestra que estás


familiarizado con conceptos universales de programación informática, como tipos de
datos, contenedores, funciones, condiciones y bucles, así como los aspectos más
importantes de la sintaxis, semántica y el entorno
de ejecución del lenguaje de programación Python.

El obtener la certificación PCEP asegura que


estás familiarizado con los medios más esenciales
proporcionados por Python 3, que te permiten
comenzar tus propios estudios en un nivel
intermedio y continuar con tu desarrollo
profesional.

La certificación PCEP te permite demostrar que no


solo estás al día con problemas relacionados con
Python, sino que también puedes solucionarlos de
manera competente.

Además, con OpenEDG Python Institute, obtienes acceso a una gran red de profesionales
de la programación en Python, un recurso valioso para resolver problemas relacionados
con Python y para desarrollar soluciones innovadoras.

La certificación PCEP – Certified Entry-Level Python Programmer es un paso


intermedio hacia la certificación PCAP – Certified Associate in Python Programming y el
punto de partida para iniciar una carrera en el desarrollo de software, programación en
Python y tecnologías relacionadas.

El obtener la certificación PCEP te ayudará a destacarte de otros candidatos y te abrirá


puertas.

Mapa de Certificaciones de OpenEDG Python Institute

Fundamentos de Python 1 - Módulo 1

Fundamentos de Python 1:
Módulo 1
Introducción a Python y a la programación de computadoras.

En este módulo, aprenderás sobre:

• Los fundamentos de la programación de computadoras, es decir, cómo funciona


la computadora, cómo se ejecuta el programa, cómo se define y construye el
lenguaje de programación.
• La diferencia entre compilación e interpretación.
• Qué es Python, cómo se posiciona entre otros lenguajes de programación y qué
distingue las diferentes versiones de Python.
1.1.1.1 Programación - Fundamentos Básicos

¿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 comprender 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 conocer 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:

• Acepte un número que represente la distancia.


• Acepte un número que represente el tiempo de viaje.
• Divida el valor anterior entre 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 lenguaje que la computadora
pueda aceptar.
La palabra clave es el lenguaje.

1.1.1.2 Programación - Fundamentos Básicos

Lenguajes naturales frente a 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 sola palabra.

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

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


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

Los comandos que reconoce son muy simples. Podemos imaginar que la computadora
responde a órdenes como "Toma este número, divídelo entre 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). Los diferentes tipos de computadoras pueden
variar según el tamaño de sus IL y las instrucciones pueden ser completamente
diferentes en diferentes modelos.

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

Ninguna computadora es actualmente


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

Cada día se crean nuevas palabras y


desaparecen las viejas. Estos lenguajes
se llaman lenguajes naturales.
1.1.1.3 Programación - Fundamentos Básicos

¿Qué compone a un lenguaje?


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

• Un alfabeto: un conjunto de símbolos utilizados para formar palabras de un


determinado lenguaje (por ejemplo, el alfabeto latino para el inglés, el alfabeto
cirílico para el ruso, el kanji para el japonés, y así sucesivamente).
• Un léxico: (también conocido como diccionario) un conjunto de palabras que el
lenguaje ofrece a sus usuarios (por ejemplo, la palabra "computadora" proviene
del diccionario en inglés, mientras que "cmoptrue" no; la palabra "chat" está
presente en los diccionarios de inglés y francés, pero sus significados son
diferentes.
• Una 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).
• Una 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.
Ambos (tanto las computadoras como los humanos) necesitamos algo más, un lenguaje
común para las computadoras y los seres humanos, o un puente entre los dos mundos
diferentes.

Necesitamos un lenguaje en el que los humanos puedan escribir sus programas y un


lenguaje que las computadoras puedan usar para ejecutar los programas, que es mucho
más complejo que el lenguaje máquina y más sencillo que el lenguaje natural.

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

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


fuente (en contraste con el código de máquina ejecutado por las computadoras). Del
mismo modo, el archivo que contiene el código fuente se llama archivo fuente.
1.1.1.4 Programación - Fundamentos Básicos | La Compilación
frente a la Interpretación

La compilación frente a la interpretación


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

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

• Alfabéticamente: un programa debe escribirse en una secuencia de comandos


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

Desafortunadamente, un programador también puede cometer errores en cada uno de


los cuatro sentidos anteriores. Cada uno de ellos puede hacer que el programa se vuelva
completamente inútil.

Supongamos que has escrito correctamente un programa. ¿Cómo persuadimos a la


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

Existen 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 máquina; ahora puedes distribuir el archivo en todo el mundo; el
programa que realiza esta traducción se llama compilador o traductor.

INTERPRETACIÓN - Tú (o cualquier usuario del código) puedes traducir el programa


fuente cada vez que se ejecute; el programa que realiza este tipo de transformación se
denomina intérprete, ya que interpreta el código cada vez que está destinado a
ejecutarse; también significa que no puede distribuir el código fuente tal como está,
porque el usuario final también necesita que el intérprete lo ejecute.

Debido a algunas razones muy fundamentales, un lenguaje de programación de alto nivel


en particular está diseñado para caer en una de estas dos categorías.
Existen muy pocos idiomas que se pueden ser tanto compilados como interpretados. Por
lo general, un lenguaje de programación se proyecta con este factor en la mente de sus
constructores: ¿Se compilará o interpretará?

1.1.1.5 Programación - Fundamentos Básicos | La Compilación


frente a la Interpretación

¿Qué hace realmente el intérprete?


Supongamos una vez más que has escrito un programa. Ahora, existe como un archivo
de computadora: un programa de computadora es en realidad una pieza de texto, por lo
que el código fuente generalmente se coloca en archivos de texto.

Nota: debe ser texto puro, sin ninguna decoración, como diferentes fuentes, colores,
imágenes incrustadas u otros medios. Ahora tienes que invocar al intérprete y dejar que
lea el archivo fuente.

El intérprete lee el código fuente de una manera que es común en la cultura occidental:
de arriba hacia 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 intérprete encuentra un error, termina su trabajo inmediatamente. El único


resultado en este caso es un mensaje de error.

El intérprete te 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 sus causas reales.

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

En otras palabras, la razón real generalmente se ubica un poco antes en el código, por
ejemplo, en el lugar donde se tuvo que informar
al intérprete de que usarías 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", puede repetirse muchas
veces, más veces que el número real de líneas en
el archivo fuente, debido a que algunas partes del
código pueden ejecutarse más de una vez).
También es posible que una parte significativa del código se ejecute con éxito antes de
que el intérprete encuentre un error. Este es el comportamiento normal en este modelo
de ejecución.

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


"interpretación"? No hay una respuesta obvia. Si lo hubiera, uno de estos modelos habría
dejado de existir hace mucho tiempo. Ambos tienen sus ventajas y sus desventajas.

1.1.1.6 Programación - Fundamentos Básicos | La Compilación


frente a la Interpretación

La compilación frente a la interpretación - ventajas y


desventajas
COMPILACIÓN
Ventajas Desventajas
• La ejecución del código traducido suele • La compilación en sí misma puede llevar
ser más rápida. mucho tiempo; es posible que no
puedas ejecutar tu código
inmediatamente después de cualquier
modificación.
• Solo el programador debe tener el • Tienes que tener tantos compiladores
compilador; el usuario final puede usar como plataformas de hardware en las
el código sin él. que deseas que se ejecute tu código.
• El código traducido se almacena en
lenguaje máquina, ya que es muy difícil
de entender, es probable que tus
propios inventos y trucos de
programación sigan siendo un secreto.

INTERPRETACIÓN
Ventajas Desventajas
• Puedes ejecutar el código en cuanto lo • No esperes que la interpretación
completes; no hay fases adicionales de incremente tu código a alta velocidad: tu
traducción. código compartirá la potencia de la
computadora con el intérprete, por lo
que no puede ser realmente rápido.
• El código se almacena utilizando el • Tanto tú como el usuario final deben
lenguaje de programación, no el de la tener el intérprete para ejecutar el
máquina; esto significa que puede código.
ejecutarse en computadoras que utilizan
diferentes lenguajes máquina; no se
compila el código por separado para
cada arquitectura diferente.

¿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 scripting, mientras que los programas
fuente codificados que los usan se llaman scripts.

1.1.2.1 Python - Una herramienta, no un reptil

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

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 en


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

Dado que Monty Python es considerado uno de los dos nutrientes fundamentales para
un programador (el otro es la pizza), el creador de Python nombró el lenguaje en honor al
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 existen muchos lenguajes de programación cuyos autores sean 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,
testers, usuarios (muchos de ellos no son
especialistas en TI) y entusiastas, pero hay
que decir que la primera idea (la semilla de
la que brotó Python) llegó a una cabeza: la
de Guido.

1.1.2.2 Python - Una herramienta, no un reptil

Un proyecto de programación por hobby


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

En Diciembre de 1989, estaba buscando un proyecto de programación de "pasatiempo"


que me mantendría ocupado durante la semana de Navidad. Mi oficina (...) estaría
cerrada, pero tenía una computadora en casa y no mucho más en mis manos. Decidí
escribir un intérprete para el nuevo lenguaje de scripting en el que había estado
pensando últimamente: un descendiente de ABC que atraería a los hackers de Unix/C.
Elegí Python como el 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 PYPL PopularitY of
Programming Language y el TIOBE
Programming Community Index.

Python no es una lengua joven. Es maduro y


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

1.1.2.3 Python - Una herramienta, no un reptil | ¿Por qué Python?

¿Qué hace especial a Python?


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

Existen 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 emplea Python.
• Es fácil de entender: a menudo, también es más fácil entender el código de otra
persona más rápido si está escrito en Python.
• Es fácil de obtener, instalar y desplegar: Python es gratuito, abierto y
multiplataforma; no todos los lenguajes pueden presumir de eso.

Por supuesto, Python también tiene sus inconvenientes:

• No es un demonio de la velocidad: Python no ofrece un rendimiento excepcional.


• En algunos casos puede ser resistente a algunas técnicas de prueba 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, el cometer errores es más difícil en
Python.

También debe señalarse que Python no es la única


solución de este tipo disponible en el mercado de
TI.

Tiene muchos seguidores, pero hay muchos que


prefieren otros lenguajes y ni siquiera consideran
Python para sus proyectos.

1.1.2.4 Python - Una herramienta, no un reptil | ¿Por qué Python?,


¿Por qué no?

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

• Perl: un lenguaje de scripting originalmente escrito por Larry Wall.


• Ruby: un lenguaje de scripting originalmente escrito por Yukihiro Matsumoto.

El primero es más tradicional, más conservador que Python, y se parece a algunos de los
buenos lenguajes antiguos derivados del lenguaje de programación C clásico.

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

Internet está lleno de foros con discusiones infinitas sobre la superioridad de uno de
estos tres sobre los otros, por 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 testers 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 existen algunos nichos
en los que Python está ausente o rara
vez se ve:

• Programación de bajo nivel (a


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

1.1.3.1 Python 2 frente a Python 3

Existe más de un Python


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

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. Grandes partes del código
pueden permanecer intactas, pero tienes que revisar todo el código para encontrar todas
las incompatibilidades posibles. Desafortunadamente, este proceso no puede ser
completamente automatizado.

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


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

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


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

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

NOTA

Si se va a comenzar un nuevo proyecto de Python, deberías usar Python 3, esta es la


versión de Python que se usará durante este curso.

Es importante recordar que puede haber


diferencias mayores o menores entre las
siguientes versiones de Python 3 (p. Ej.,
Python 3.6 introdujo claves de
diccionario ordenadas de forma
predeterminada en la implementación
de CPython). La buena noticia es que
todas las versiones más nuevas de
Python 3 son compatibles con las
versiones anteriores de Python 3.
Siempre que sea significativo e
importante, 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 y
Python 3.8.

1.1.3.2 Existe más de un Python: CPython y Cython

Python alias CPython


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

En primer lugar, están los Pythons que se


mantienen por 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 provienen
del PSF están escritos en el lenguaje "C". Existen muchas razones para este enfoque. Una
de ellas (probablemente la más importante) es que gracias a ello, Python puede ser
portado y migrado fácilmente a todas las plataformas con la capacidad de compilar y
ejecutar programas en lenguaje "C" (virtualmente todas las plataformas tienen esta
característica, lo que abre mucha expansión y oportunidades para Python).

Esta es la razón por la que la implementación de PSF a menudo se denomina CPython.


Este es el Python más influyente entre todos los Pythons del mundo.

Cython
Otro miembro de la familia Python es Cython.

Cython es una de las posibles soluciones al


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

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

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


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

1.1.3.3 Existe más de un Python: Jython, PyPy, y RPython

Jython
Otra versión de Python se llama Jython.
"J" es de "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. ¿Puedes resolverlo?

Es el logotipo de PyPy - un Python


dentro de un Python. En otras
palabras, representa un entorno de
Python escrito en un lenguaje similar a Python llamado RPython (Restricted Python). En
realidad es un subconjunto de Python.

El código fuente de PyPy no se ejecuta de manera interpretativa, sino que se traduce al


lenguaje de programación C y luego se ejecuta por separado.

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

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

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

Hay muchos más Pythons diferentes en el mundo. Los encontrarás sí los buscas,
pero este curso se centrará en CPython.
1.2.1.1 Comienza tu viaje con Python

Cómo obtener y utilizar Python


Existen 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 ensamblar herramientas específicas con el


sistema y muchas de estas herramientas, como los administradores de paquetes, a
menudo están escritas en Python. Algunas partes de los entornos gráficos disponibles en
el mundo de Linux también pueden usar Python.

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

python3

En el prompt del 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 utilizar 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 de Linux pueden descargar una copia
en https://www.python.org/downloads/.
1.2.1.2 Comienza tu viaje con Python | Descargando e instalando
Python

Descargando e instalando Python


Debido a que el navegador le dice al sitio web al 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
desea.

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

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

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

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

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


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

Comenzando tu trabajo con Python


Ahora que tienes Python 3 instalado, es hora de verificar si funciona y de utilizarlo por
primera vez.

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

Existen 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 (debe 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 puedas ejecutar tu código recién escrito y detenerlo por la
fuerza cuando se sale de control.
• Una herramienta llamada depurador, capaz de ejecutar tu código paso a paso y te
permite inspeccionarlo en cada momento de su ejecución.

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


una aplicación muy simple pero extremadamente útil llamada IDLE.
IDLE es un acrónimo de: Integrated Development and Learning Environment (Desarrollo
Integrado y Entorno de Aprendizaje).

Navega por los menús de tu sistema operativo, encuentra IDLE en algún lugar debajo de
Python 3.x y ejecútalo. Esto es lo que deberías ver:

1.2.1.4 Comienza tu viaje con Python

Cómo escribir y ejecutar tu primer programa


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

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

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

Esta es la ventana del editor. Su único propósito es ser un lugar de trabajo en el que se
trate tu código fuente. No confundas la ventana del editor con la ventana del 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 fuente.

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

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

Cómo escribir y ejecutar tu primer programa


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

La línea se ve así:

print("Hisssssss...")

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

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


discusión detallada en el siguiente capítulo.

Echa un vistazo más de cerca a las comillas. Estas son la forma más simple de las comillas
(neutrales, rectas, etc.) que se usan comúnmente en los archivos fuente. No intentes
utilizar citas tipográficas (curvadas, rizadas, etc.), utilizadas por los procesadores de texto
avanzados, ya que Python no las acepta.

Si todo sale 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 ejecutará de manera correcta y mostrará Hisssssss... en la


consola.

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

Ahora cierra ambas ventanas y vuelve al escritorio.


1.2.1.6 Comienza tu viaje con Python

Cómo estropear y arreglar tu código


Ahora ejecuta IDLE nuevamente.

• Haz clic en File, Open, señala el archivo que guardaste anteriormente y deja que
IDLE lo lea de nuevo.
• 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 muy útil.

• Primero, quita el paréntesis de cierre.


• Luego ingresa el paréntesis nuevamente.

Tu código debería parecerse al siguiente:

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

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


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

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


modificado. Sigue las instrucciones.

1.2.1.7 Comienza tu viaje con Python

Cómo estropear y arreglar tu código


Observa todas las ventanas con cuidado.

Aparece una nueva ventana, dice que el intérprete ha encontrado un EOF (fin-de-archivo)
aunque (en su opinión) el código debería contener algo más de texto.

La ventana del editor muestra claramente donde ocurrió.


Ahora arregla el código. Debe verse así:

print("Hisssssss...")

Ejecútalo para ver si sigue funcionando.

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 la instrucción.

1.2.1.8 Comienza tu viaje con Python

Cómo estropear y arreglar tu código


Es posible que hayas notado que el mensaje de error generado para el error anterior es
bastante diferente del primero.
Esto se debe a que la naturaleza del error es diferente y el error se descubre en
una etapa diferente de la interpretación.

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

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

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

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


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

Finalización del Módulo

¡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
cubiertos y con los que te has familiarizado en el Módulo 1:

• Los fundamentos de la programación de computadoras, es decir, cómo funciona


la computadora, cómo se ejecuta el programa, cómo se define y construye el
lenguaje de programación.
• 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, el cual te
ayudará a evaluar lo que has aprendido
hasta ahora.

Fundamentos de Python 1: Módulo 2

Fundamentos de Python 1:
Módulo 2
Tipos de datos, variables, operaciones básicas de entrada y salida, operadores básicos

En este módulo, aprenderás:

• Cómo escribir y ejecutar programas simples en Python.


• Qué son los literales, operadores y expresiones en Python.
• Qué son las variables y cuáles son las reglas que las gobiernan.
• Cómo realizar operaciones básicas de entrada y salida.

2.1.1.1 Tu primer programa

¡Hola, Mundo!
Es hora de comenzar a escribir código real y funcional en Python. Por el momento será
muy sencillo.
Como se muestran algunos conceptos y términos fundamentales, estos fragmentos de
código no serán complejos ni difíciles.

Ejecuta el código en la ventana del editor a la derecha. Si todo sale bien, verás 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 el texto contenido
entre comillas en la ventana de la consola IDLE. El código que has ejecutado debería
parecerte familiar. Viste algo muy similar cuando te guiamos a través de la configuración
del entorno IDLE.

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

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

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

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

print("¡Hola, Mundo!")

2.1.1.2 Tu primer programa

La función print()
Observa 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 que sus
parientes matemáticos.

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

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

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

¿De dónde provienen las funciones?

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

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

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

2.1.1.3 Tu primer programa

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

• Un efecto.
• Un resultado.

También existe un tercer componente de la función, muy importante, el o


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

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

print("¡Hola, Mundo!")

A pesar del número de argumentos necesarios o proporcionados, las funciones de


Python demandan fuertemente la presencia de un par de paréntesis - el de apertura y de
cierre, respectivamente.

Si deseas entregar uno o más argumentos a una función, colócalos dentro de los
paréntesis. Si vas a utilizar una función que no tiene ningún argumento, aún tiene que
tener los paréntesis.

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

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

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

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

2.1.1.4 Tu primer programa

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

print("¡Hola, Mundo!")

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

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

Casi cualquier cosa que ponga dentro de las comillas se tomará de manera literal, no
como código, sino como datos. Intenta jugar con esta cadena en particular - puedes
modificarla. Ingresa contenido nuevo o borra parte del contenido existente.
Existe más de una forma de como especificar una cadena dentro del código de Python,
pero por ahora, esta será suficiente.

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

2.1.1.5 Tu primer programa

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

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

print("¡Hola, Mundo!")

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

nombre_función(argumento)

Veamos:

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


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

LABORATORIO

Tiempo Estimado
5-10 minutos

Nivel de Dificultad
Muy fácil

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

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

En tu primer laboratorio:

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


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

2.1.1.7 Tu primer programa

La función print()
Tres preguntas importantes deben ser respondidas antes de continuar:

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


El efecto es muy útil y espectacular. La función:

• Toma los argumentos (puede aceptar más de un argumento y también puede


aceptar menos de un argumento).
• Los convierte en un formato legible para el ser humano si es necesario (como
puedes sospechar, las cadenas no requieren esta acción, ya que la cadena ya es
legible).
• Envía los datos resultantes al dispositivo de salida (generalmente la consola); en
otras palabras, cualquier cosa que se ponga en la función de print() aparecerá en
la pantalla.

No es de extrañar entonces, que de ahora en adelante, utilizarás print() muy


intensamente para ver los resultados de tus operaciones y evaluaciones.

2. ¿Qué argumentos espera print() ?

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

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

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

2.1.1.8 Tu primer programa

La función print() - instrucciones


Ya has visto un programa de computadora que contiene una invocación de función. La
invocación de una función es uno de los muchos tipos posibles de instrucciones de
Python.

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
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. Ejecútalo y observa lo que
aparece 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ó.

salida

Esta es una buena oportunidad para hacer algunas observaciones:

• El programa invoca la función print() dos veces, como puedes ver hay dos líneas
separadas en la consola: esto significa que print() comienza su salida desde una
nueva línea cada vez que comienza su ejecución. Puedes cambiar este
comportamiento, pero también puedes usarlo a tu favor.
• Cada invocación de print() contiene una cadena diferente, como su argumento
y el contenido de la consola lo reflejan, esto significa que las instrucciones en el
código se ejecutan en el mismo orden en que se colocaron en el archivo fuente;
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ó.")

2.1.1.9 Tu primer programa

La función print() - instrucciones


Hemos cambiado un poco el ejemplo: hemos agregado una invocación vacía de la
función print() . La llamamos vacía porque no hemos agregado ningún argumento a la
función.

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

¿Qué ocurre?

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

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


Vino la lluvia y se la llevó.

salida

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 línea en la consola de salida. Enseguida
mostraremos otra manera.

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


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

2.1.1.10 Tu primer programa

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


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

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

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

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

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


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

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

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


línea).

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ó.
salida
Como se puede observar, aparecen dos nuevas líneas en la canción infantil, en los lugares
donde se ha utilizado \n .

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


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

2.1.1.11 Tu primer programa

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


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

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

print("\")

Mientras que esta no lo hará:

print("\\")

2. No todos los pares de escape (la diagonal invertida junto con otro carácter) significan
algo.

Experimenta con el código en el editor, ejecútalo y observa lo que sucede.

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


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

2.1.1.12 Tu primer programa

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


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

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


print("La Witsi Witsi Araña" , "subió" , "a 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 observar 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ó a su telaraña.

salida

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

Dos conclusiones surgen de este ejemplo:

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

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

2.1.1.13 Tu primer programa

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


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

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

La forma en que pasamos los argumentos a la función print() es la más común en


Python, y se denomina manera posicional (este nombre proviene del hecho de que el
significado del argumento está dictado por su posición, por ejemplo, el segundo
argumento se emitirá después del primero, y no al revés).

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

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


print("Monty Python.")

2.1.1.14 Tu primer programa

La función print() - los argumentos de palabra 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 cómo funciona. Siéntete libre
de utilizarlo en tus propios programas.

El mecanismo se llama argumentos de palabra 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 palabra clave que se pueden utilizar para
estos propósitos. El primero de ellos se llama end .

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

Para utilizarlo es necesario conocer algunas reglas:

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

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


igualado a una cadena que contiene un espacio.

Ejecuta el código para ver cómo funciona.

La consola ahora debería mostrar el siguiente texto:

Mi nombre es Python. Monty Python.


salida

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

2.1.1.15 Tu primer programa

La función print() - los argumentos de palabra clave


Y ahora, es el momento de intentar algo más difícil.

Si observas detenidamente, verás que hemos utilizado el argumento end , pero su


cadena asignada está vacía (no contiene ningún carácter).

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

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


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

La consola ahora debería mostrar el siguiente texto:

Mi nombre es Monty Python.

salida

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

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


longitud. Experimenta con ello si gustas.

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


print("Monty Python.")

2.1.1.16 Tu primer programa

La función print() - los argumentos de palabra clave


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

El argumento de palabra clave que puede hacer esto se denomina sep (separador).

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


El argumento sep entrega el siguiente resultado:

Mi-nombre-es-Monty-Python.

salida

La función print() ahora utiliza un guion, en lugar de un espacio, para separar los
argumentos generados.

Nota: el valor del argumento sep también puede ser una cadena vacía. Pruébalo tu
mismo.

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

2.1.1.17 Tu primer programa

La función print() - los argumentos de palabra clave


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

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


entre end y sep .

¿Puedes predecir la salida?

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

Ahora que comprendes la función print() , estás listo para 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")

2.1.1.18 LABORATORIO: La función print()

LABORATORIO

Tiempo Estimado
5-10 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() .

Salida Esperada
Fundamentos***Programación***en...Python

salida

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

2.1.1.19 LABORATORIO: Dando formato a la salida

LABORATORIO

Tiempo Estimado
5-15 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:

• 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: "cadena" *
2 producirá "cadenacadena" (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 minúscula
a mayúscula, Print ) - ¿Qué sucede ahora?
• Reemplaza algunas de las comillas por apóstrofes; observa lo que pasa
detenidamente.

print(" *")
print(" * *")
print(" * *")
print(" * *")
print("*** ***")
print(" * *")
print(" * *")
print(" *****")

2.1.1.20 RESUMEN DE LA SECCIÓN

Puntos Clave
1. La función print() es una función integrada imprime/envía un mensaje específico a la
pantalla/ventana de consola.

2. Las funciones integradas, al contrario de las funciones definidas por el usuario, están
siempre disponibles y no tienen que ser importadas. Python 3.7.1 viene con 69 funciones
incorporadas. Puedes encontrar su lista completa en orden alfabético en Python
Standard Library.

3. Para llamar a una función (invocación de función), debe utilizarse el nombre de la


función seguido de un paréntesis. Puedes pasar argumentos a una función colocándolos
dentro de los paréntesis. Se Deben separar los argumentos con una coma, por
ejemplo, print("¡Hola,", "Mundo!") . una función print() "vacía" imprime una línea
vacía a la pantalla.
4. Las cadenas de Python están delimitadas por comillas, por ejemplo, "Soy una
cadena" , o 'Yo soy una cadena, también' .

5. Los programas de computadora son colecciones de instrucciones. Una instrucción es


un comando para realizar una tarea específica cuando se ejecuta, por ejemplo, para
imprimir un determinado mensaje en la pantalla.

6. En las cadenas de Python, la barra diagonal inversa ( \ ) es un carácter especial que


anuncia que el siguiente carácter tiene un significado diferente, por
ejemplo, \n (el carácter de nueva línea) comienza una nueva línea de salida.

7. Los argumentos posicionales son aquellos cuyo significado viene dictado por su
posición, por ejemplo, el segundo argumento se emite después del primero, el tercero se
emite después del segundo, etc.

8. Los argumentos de palabra clave son aquellos cuyo significado no está dictado por su
ubicación, sino por una palabra especial (palabra clave) que se utiliza para identificarlos.

9. Los parámetros end y sep se pueden usar para dar formato la salida de la
función print() . El parámetro sep especifica el separador entre los argumentos
emitidos (por ejemplo, print("H", "E", "L", "L", "O", sep="-") , mientras que el
parámetro end especifica que imprimir al final de la declaración de impresión.

2.2.1.1 Literales de Python

Literales - los datos en si mismos


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

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

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

Observa los siguientes dígitos:

123

¿Puedes adivinar qué valor representa? Claro que puedes - es ciento veintitrés.

Que tal este:

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.

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

2.2.1.2 Literales de Python

Literales - los datos en si mismos


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 máquina (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)
2.2.1.3 Literales de Python

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 número entero.

El proceso es casi como usar lápiz y papel, es simplemente una cadena de dígitos que
conforman el número, pero hay una condición, no se deben insertar caracteres que no
sean dígitos dentro del número.

Tomemos por ejemplo, el número once millones ciento once mil ciento once. Si tomaras
ahorita un lápiz en tu mano, escribirías el siguiente número: 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.
Está 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 .

2.2.1.4 Literales de Python

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 número entero esta precedido por un código 0O o 0o (cero-o), el número 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)

2.2.1.5 Literales de Python

Flotantes
Ahora es tiempo de hablar acerca de otro tipo, el cual esta designado para representar y
almacenar los números que (como lo diría un matemático) tienen una parte decimal no
vacía.
Son números que tienen (o pueden tener) una parte fraccionaria después del punto
decimal, y aunque esta definición es muy pobre, es suficiente para lo que se desea
discutir.

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

2.5
-0.4

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

Python no lo aceptará, o (en casos poco probables) puede malinterpretar el número,


debido a que la coma tiene su propio significado en Python.

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

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

0.4

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

En esencia, el valor 0.4 se puede escribir como:

.4

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

4.

Esto no cambiará su tipo ni su valor.

2.2.1.6 Literales de Python

Enteros frente a 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.

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.

2.2.1.7 Literales de Python

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

Una constante de física denominada "La Constante de Planck" (denotada como h), de
acuerdo con los libros de texto, tiene un valor de: 6.62607 x 10-34.
Si se quisiera utilizar en un programa, se debería escribir de la siguiente manera:

6.62607E-34

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

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

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


0.0000000000000000000001

Cuando se corre en Python:


print(0.0000000000000000000001)

Este es el resultado:

1e-22
salida

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

2.2.1.8 Python literals

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

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

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

Sin embargo, hay una cuestión. ¿Cómo se puede codificar una comilla dentro de una
cadena que ya está delimitada por comillas?

Supongamos que se desea mostrar un muy sencillo mensaje:

Me gusta "Monty Python"

¿Cómo se puede hacer esto sin generar un error? Existen dos posibles soluciones.
La primera se basa en el concepto ya conocido del carácter de escape, el cual recordarás
se utiliza empleando la diagonal invertida. La diagonal invertida puede también escapar
de la comilla. Una comilla precedida por una diagonal invertida cambia su significado, no
es un limitador, simplemente es una comilla. Lo siguiente funcionará como se desea:

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

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

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

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

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

Este ejemplo funcionará también:

print('Me gusta "Monty Python"')

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

2.2.1.9 Literales de Python

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

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

Intenta imprimir una cadena que contenga el siguiente mensaje:

I'm Monty Python.

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

Revisar

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

print("I'm Monty Python.")

Como se puede observar, la diagonal invertida es una herramienta muy poderosa, puede
escapar no solo comillas, sino también apóstrofes.
Ya se ha mostrado, pero se desea hacer énfasis en este fenómeno una vez mas - una
cadena puede estar vacía - puede no contener carácter alguno.

Una cadena vacía sigue siendo una cadena:

''

""

2.2.1.10 Literales de Python

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 Álgebra Booleana - una parte del álgebra que hace uso de dos
valores: Verdadero y Falso , denotados como 1 y 0 .

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


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

Afortunadamente, las computadoras solo conocen dos tipos de respuestas:

• Si, esto es verdad.


• No, esto es falso.

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

Python, es entonces, un reptil binario.

Estos dos valores booleanos tienen denotaciones estrictas en Python:

True
False

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

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


print(True > False)
print(True < False)

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

2.2.1.11 LABORATORIO: Literales de Python - Cadenas

LABORATORIO

Tiempo Estimado
5-10 minutos

Nivel de Dificultad
Fácil

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

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

Salida Esperada
"Estoy"
""aprendiendo""
"""Python"""
salida

2.2.1.12 RESUMEN DE LA SECCIÓN

Puntos Clave
1. Literales son notaciones para representar valores fijos en el código. Python tiene varios
tipos de literales, es decir, un literal puede ser un número por ejemplo, 123 ), o una
cadena (por ejemplo, "Yo soy un literal.").
2. El Sistema Binario es un sistema numérico que emplea 2 como su base. Por lo tanto,
un número binario está compuesto por 0s y 1s únicamente, por ejemplo, 1010 es 10 en
decimal.

Los sistemas de numeración Octales y Hexadecimales son similares pues emplean 8 y 16


como sus bases respectivamente. El sistema hexadecimal utiliza los números decimales
más seis letras adicionales.

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

4. Los números Punto-Flotante (o simplemente flotantes) son otro tipo numérico que
soporta Python. Son números que contienen (o son capaces de contener) una parte
fraccionaria, por ejemplo, 1.27 .

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

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

EXTRA

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

Ejercicio 1

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

"Hola ", "007"

Revisar

Ambos son cadenas.

Ejercicio 2

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

"1.5", 2.0, 528, False

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

Ejercicio 3

¿Cuál es el valor en decimal del siguiente número en binario?

1011

Revisar

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

2.3.1.1 Operadores: herramientas para la manipulación de datos

Python como una calculadora


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

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

print(2+2)

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

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


operadores.

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

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


los operadores y expresiones.

Los operadores básicos


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

Por ejemplo, como en la aritmética, el signo de + (más) 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.

2.3.1.2 Operadores: herramientas de manipulación de datos

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.

Nota: En los ejemplos, los dobles asteriscos están rodeados de espacios, no es obligatorio
hacerlo pero hace que el código sea mas legible.

Los ejemplos muestran una característica importante de los operadores numéricos de


Python.

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

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

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


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

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

print(2 ** 3)
print(2 ** 3.)
print(2. ** 3)
print(2. ** 3.)
2.3.1.3 Operadores: herramientas para la manipulación de datos

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 frente a flotante aún funciona.

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

2.3.1.4 Operadores: herramientas para la manipulación de datos

Operadores Aritméticos: división entera


Un símbolo de // (doble diagonal) es un operador de división entera. Difiere del
operador estándar / en dos detalles:
• El resultado carece de la parte fraccionaria, está ausente (para los enteros), o
siempre es igual a cero (para los flotantes); esto significa que los resultados
siempre son redondeados.
• Se ajusta a la regla entero frente a 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 mas 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 mas


cercano del resultado de la división no redondeada.

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

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

print(-6 // 4)
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.

2.3.1.5 Operadores: herramientas para la manipulación de datos

Operadores: residuo (módulo)


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

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


poco confuso.

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

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

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

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


programación.

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

print(14 % 4)

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

• 14 // 4 da como resultado un 3 → esta es la parte entera, es decir el cociente.


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

El siguiente ejemplo es un poco 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)

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.

2.3.1.6 Operadores: herramientas para la manipulación de datos

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

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

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

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

El operador de resta, operadores unarios y binarios


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

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

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


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

Por esta razón, el operador de resta es considerado uno de los operadores binarios, así
como los demás operadores de suma, multiplicación y división.
Pero el operador negativo puede ser utilizado de una forma diferente, observa la ultima
línea de código del siguiente fragmento:

print(-4 - 4)
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?

2.3.1.7 Operadores: herramientas para la manipulación de datos

Operadores y sus prioridades


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

También, muy seguido encontrarás más de un operador en una expresión, y entonces


esta presunción ya no es tan obvia.

Considera la siguiente expresión:

2 + 3 * 5

Probablemente recordarás de la escuela que las multiplicaciones preceden a las sumas.

Seguramente recordarás 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á como funciona. Observa:

print(9 % 6 % 2)

Existen dos posibles maneras de evaluar la expresión:

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


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

Ejecuta el ejemplo y observa lo que se obtiene.

El resultado debe ser 1 . El operador tiene un enlazado del lado izquierdo. Pero hay una
excepción interesante.

2.3.1.8 Operadores: herramientas para la manipulación de datos

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?

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


lado derecho.
2.3.1.9 Operadores: herramientas para la manipulación de datos

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 más alta (1) a la más baja (4)
prioridad.

Intenta solucionar la siguiente expresión:

print(2 * 3 % 5)

Ambos operadores ( * y % ) tienen la misma prioridad, el resultado solo se puede obtener


conociendo el sentido del enlazado. ¿Cuál será el resultado?

Revisar

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

De acuerdo con las reglas aritméticas, las sub-expresiones dentro de los paréntesis
siempre se calculan primero.
Se pueden emplear tantos paréntesis como se necesiten, y seguido son utilizados
para mejorar la legibilidad de una expresión, aun si no cambian el orden de las
operaciones.

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

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

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


función print() ?

Revisar

10.0

2.3.1.10 RESUMEN DE LA SECCIÓN

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

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

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


clásica: regresa un flotante siempre), % (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 número
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 .
8. Los operadores de exponenciación utilizan enlazado del lado derecho, por ejemplo, 2
** 2 ** 3 = 256 .

Ejercicio 1

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

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

Revisar

16 8.0 8

Ejercicio 2

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

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

Revisar

-0.5 0.5 0 -1

Ejercicio 3

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

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

Revisar

-2 2 512

2.4.1.1 Variables: cajas en forma de datos

¿Qué son las Variables?


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

Ya hemos visto que se pueden hacer operaciones aritméticas con estos números: sumar,
restar, etc. Esto se hará una infinidad de veces en un programa.
Pero es normal preguntar como es que se pueden almacenar los resultados de estas
operaciones, para poder emplearlos en otras operaciones, y así sucesivamente.

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


producir resultados subsecuentes?

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

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

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

Comencemos con lo relacionado al nombre de la variable.

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


debes decidir cuantas variables deseas utilizar en tu programa.

También las debes de nombrar.

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

• El nombre de la variable debe de estar compuesto por MAYÚSCULAS, 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).
2.4.1.2 Variables: cajas con forma de datos

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 , dias_para_navidad , ElNombreEsTanLar


goQueSeCometeranErroresConEl , _ .

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

Estos nombres de variables también son correctos:

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

Ahora veamos algunos nombres incorrectos:

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

NOTA

PEP 8 -- Style Guide for Python Code recomienda la siguiente convención de


nomenclatura para variables y funciones en Python:

• Los nombres de las variables deben estar en minúsculas, con palabras separadas
por guiones bajos para mejorar la legibilidad (por ejemplo: var , mi_variable ).
• Los nombres de las funciones siguen la misma convención que los nombres de las
variables (por ejemplo: fun , mi_función ).
• También es posible usar letras mixtas (por ejemplo: miVariable ), pero solo en
contextos donde ese ya es el estilo predominante, para mantener la
compatibilidad retroactiva con la convención adoptada.

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

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


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

Son llamadas palabras clave o (mejor dicho) palabras reservadas. Son reservadas
porque no se deben utilizar como nombres: ni para variables, ni para funciones, ni para
cualquier otra cosa que se desee crear.

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

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


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

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

import

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

Import

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

2.4.1.3 Variables: cajas con forma de datos

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

Cualquier cosa.

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

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

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

RECUERDA
Una variable se crea cuando se le asigna un valor. A diferencia de otros lenguajes de
programación, no es necesario declararla.

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


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

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


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

Observa el siguiente fragmento de código:


var = 1
print(var)

Consiste de dos simples instrucciones:

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

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

Revisar

2.4.1.4 Variables: cajas con forma de datos

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

var = 1
account_balance = 1000.0
client_name = 'John Doe'
print(var, account_balance, client_name)
print(var)

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 ocasionará 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.8.5"
print("Versión de Python: " + var)

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

Revisar

Versión de Python: 3.8.5

2.4.1.5 Variables: cajas con forma de datos

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
salida

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

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


llamada var .

De manera 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.
2.4.1.6 Variables: cajas con forma de datos

Resolviendo problemas matemáticos simples


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

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

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

a = 3.0

b = 4.0

c = (a ** 2 + b ** 2) ** 0.5

print("c =", c)

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

√ (x) = x(½)
y

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

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

Revisar

c = 5.0

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

2.4.1.7 LABORATORIO: Variables


LABORATORIO

Tiempo Estimado
10 minutos

Nivel de Dificultad
Fácil

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

Escenario
A continuación una historia:

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

Tu tarea es:

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


• Asignar valores a las variables. El valor debe de ser igual al número 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 total_manzanas y se debe
igualar a la suma de las tres variables anteriores.
• Imprime el valor almacenado en total_manzanas 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, "Número Total de Manzanas:" y total_manzanas .

2.4.1.8 Variables: cajas con forma de datos

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

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:

sheep = sheep + 1

Python ofrece una manera más corta de escribir operaciones como estas, lo cual se
puede codificar de la siguiente manera:
x *= 2

sheep += 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

It can be simplified and shown as follows:

variable op= expresión

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

i = i + 2 * j ⇒ i += 2 * j
var = var / 2 ⇒ var /= 2
rem = rem % 10 ⇒ rem %= 10
j = j - (i + var + rem) ⇒ j -= (i + var + rem)
x = x ** 2 ⇒ x **= 2

2.4.1.9 LABORATORIO: Variables, un sencillo convertidor


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 milla equivale aproximadamente a 1.61 kilómetros,


complementa el programa en el editor para que convierta de:

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

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


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

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

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

TIP

Hay una cosa interesante más 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 número 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 número de decimales que se desean mostrar). Se hablará más 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, 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. Sé curioso.

Resultado Esperado
7.38 millas son 11.88 kilómetros
12.25 kilómetros son 7.61 millas
salida
kilometers = 12.25
miles = 7.38

miles_to_kilometers = ###
kilometers_to_miles = ###

print(miles, "millas son", round(miles_to_kilometers, 2),


"kilómetros")
print(kilometers, "kilómetros son", round(kilometers_to_miles, 2),
"millas")

2.4.1.10 LABORATORIO: Operadores y expresiones


LABORATORIO

Tiempo Estimado
10-15 minutos

Nivel de Dificultad
Fácil

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

3x3 - 2x2 + 3x - 1

El resultado debe ser asignado a y .

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


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

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

Datos de Prueba
Entrada de Muestra

x = 0
x = 1
x = -1

Salida Esperada

y = -1.0
y = 3.0
y = -9.0
salida

x = # codifica aquí tus datos de prueba


x = float(x)
# escribe tu código aquí
print("y =", y)

2.4.1.11 RESUMEN DE LA SECCIÓN

Puntos Clave

1. Una variable es una ubicación nombrada reservada para almacenar valores en la


memoria. Una variable es creada o inicializada automáticamente cuando se le asigna un
valor por primera vez. (2.1.4.1)
2. Cada variable debe de tener un nombre único - un identificador. Un nombre válido
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. (2.1.4.1)

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


necesita declarar variables en él. (2.1.4.3) 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 . (2.1.4.8)

5. Se les puede asignar valores nuevos a variables ya existentes utilizando el operador de


asignación o un operador abreviado, por ejemplo (2.1.4.5):

var = 2
print(var)

var = 3
print(var)

var += 1
print(var)

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


función print() para mostrar o imprimir los resultados, por ejemplo: (2.1.4.4)
var = "007"

print("Agente " + var)

Ejercicio 1

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

var = 2
var = 3
print(var)

Revisar

Ejercicio 2

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


my_var
m
101
averylongvariablename
m101
m 101
Del
del
Revisar

my_var

101 # incorrecto (comienza con un dígito)

averylongvariablename

m101

m 101 # incorrecto (contiene un espacio)

Del

del # incorrecto (es una palabra clave reservada)

Ejercicio 3

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

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

Revisar

11

Ejercicio 4

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

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

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

2.5.1.1 Comentarios

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


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

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


un comentario.

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

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


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

En Python, un comentario es un texto que comienza con el símbolo # y se extiende hasta


el final de la línea.

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

Justo como el siguiente código:

# Esta programa calcula la hipotenusa (c)


# a y b son las longitudes de los catetos
a = 3.0
b = 4.0
c = (a ** 2 + b ** 2) ** 0.5 # se utiliza ** en lugar de la raíz
cuadrada.
print("c =", c)

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


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

Por ejemplo, si una variable determinada está diseñada para almacenar el área de un
cuadrado, el nombre area_cuadrado será muchísimo mejor que tia_juana .
El nombre dado a la variable se puede definir como auto-comentable.

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

# Este es un programa de prueba.


x = 1
y = 2
# y = y + x
print(x + y)

Esto es frecuentemente realizado cuando se esta probando un programa, con el fin de


aislar un fragmento de código donde posiblemente se encuentra un error.

TIP

Si deseas comentar o descomentar rápidamente varias líneas de código, selecciona las


líneas que deseas modificar y utiliza el siguiente método abreviado de
teclado: CTRL + / (Windows) or CMD + / (Mac OS).Es un truco muy útil, ¿no? Intenta este
código en Sandbox.

2.5.1.2 LABORATORIO: Comentarios


LABORATORIO

Tiempo Estimado
5 minutos

Nivel de Dificultad
Muy Fácil

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

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

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

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


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

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

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


# este programa fue escrito hace dos días

a = 2 # número de horas
seconds = 3600 # número de segundos en una hora

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


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

#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

2.5.1.3 RESUMEN DE SECCIÓN

Puntos Clave

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

2. Si deseas colocar un comentario que abarque varias líneas, es necesario colocar un # al


inicio de cada línea. Además, se puede utilizar un comentario para marcar un fragmento
de código que no es necesaria en el momento y no se desea ejecutar. (observa la última
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()


# print("Soy Python.")

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

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

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

Ejercicio 1

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

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

Revisar

Cadena #2

Ejercicio 2

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

# Esto es
un comentario
en varias líneas #

print("¡Hola!")

Revisar

SyntaxError: invalid syntax

2.6.1.1 Cómo hablar con una computadora

La función input()
Ahora se introducirá una nueva función, la cual pareciese ser un reflejo de la
función print() .
¿Por qué? Bueno, print() envía datos a la consola.

Esta nueva función obtiene datos de ella.

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


que regresa un valor muy utilizable.

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

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

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

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

Observa el ejemplo:

print("Dime algo...")
anything = input()
print("Mmm...", anything, "...¿en serio?")

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

Nota:

• El programa solicita al usuario que inserte algún dato desde la consola


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

Intenta ejecutar el código y permite que la función te muestre lo que puede hacer.
2.6.1.2 Cómo hablar con una computadora

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:

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


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

Nota:

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

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


claro.

El resultado de la función input()


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

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

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

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


something = anything ** 2.0
print(anything, "al cuadrado es", something)

2.6.1.3 Cómo hablar con una computadora

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>
something = anything ** 2.0
TypeError: unsupported operand type(s) for ** or pow(): 'str' and
'float'
salida

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.

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


something = anything ** 2.0
print(anything, "al cuadrado es", something)

2.6.1.4 Cómo hablar con una computadora

Conversión de datos (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 cuál es su función:

• La función int() toma un argumento (por ejemplo, una cadena: int(string) ) e


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

Esto es muy simple y muy efectivo. Sin embargo, estas funciones se pueden invocar
directamente pasando el resultado de la función input() directamente. No hay
necesidad de emplear variables como almacenamiento intermedio.
Se ha implementado esta idea en el editor, observa el código.

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


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

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

Prueba con diferentes valores, pequeños, grandes, negativos y positivos. El cero también
es un buen valor a introducir.

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


something = anything ** 2.0
print(anything, "al cuadrado es", something)

2.6.1.5 Cómo hablar con una computadora

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


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

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

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

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

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


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

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

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

Ejecútalo de nuevo e intenta introducir valores negativos.

El programa desafortunadamente, no reacciona correctamente a este error.

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


Toma en cuenta que en el programa que puede ver en el editor, la variable hypo se usa
con un solo propósito: guardar el valor calculado entre la ejecución de la línea de código
contigua.

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:

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


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

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


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

2.6.1.6 Cómo hablar con una computadora: operadores de cadenas

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 signo 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. Por supuesto, puede ser utilizado
más de una vez en una misma expresión, y en tal contexto se comporta con enlazado del
lado izquierdo.

En contraste con el operador aritmético, el operador de concatenación no es


conmutativo, por ejemplo, "ab" + "ba" no es lo mismo que "ba" + "ab" .
No olvides, si se desea que el signo + sea un concatenador, no un sumador, solo se debe
asegurar que ambos argumentos sean cadenas.

No se pueden mezclar los tipos de datos aquí.

Este es un programa sencillo que muestra como funciona el signo + como concatenador:

fnam = input("¿Me puedes dar tu nombre por favor? ")


lnam = input("¿Me puedes dar tu apellido por favor? ")
print("Gracias.")
print("\nTu nombre es " + fnam + " " + lnam + ".")

Nota: El utilizar + para concatenar cadenas te permite construir la salida de una manera
más precisa, en comparación de utilizar únicamente la función print() , aún cuando se
enriquezca con los argumentos end= y sep= .

Ejecuta el código y comprueba si la salida es igual a tus predicciones.

2.6.1.7 Cómo hablar con una computadora: operadores de cadenas

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.

string * number
number * string

Replica la cadena el número de veces indicado por el número.

Por ejemplo:

• "James" * 3 produce "JamesJamesJames"


• 3 * "an" produce "ananan"
• 5 * "2" (o "2" * 5 ) produce "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!

2.6.1.8 Cómo hablar con una computadora: operadores de cadenas

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

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:

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


leg_b = float(input("Inserta la longitud del segundo cateto: "))
print("La longitud de la hipotenusa es " + str((leg_a**2 + leg_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.

2.6.1.9 LABORATORIO: Entradas y salidas simples

LABORATORIO

Tiempo Estimado
5-10 minutos
Nivel de Dificultad
Fácil

Objetivos
• Familiarizarse con la entrada y salida de datos en Python.
• Evaluar expresiones simples.

Escenario
La tarea es completar el código para evaluar y mostrar el resultado de cuatro operaciones
aritméticas básicas.

El resultado debe ser mostrado en consola.

Quizá no podrás proteger el código de un usuario que intente dividir entre cero. Por
ahora, no hay que preocuparse por ello.

Prueba tu código - ¿Produce los resultados esperados?

No te mostraremos ningún dato de prueba, eso sería demasiado sencillo.

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

2.6.1.10 LABORATORIO: Operadores y expresiones

LABORATORIO

Tiempo Estimado
20 minutos

Nivel de Dificultad
Intermedio
Objetivos
• Familiarizarse con los conceptos de números, operadores y expresiones
aritméticas en Python.
• Comprender la precedencia y asociatividad de los operadores de Python, así como
el correcto uso de los paréntesis.

Escenario
La tarea es completar el código para poder evaluar la siguiente expresión:

El resultado debe de ser asignado a y . Se cauteloso, observa los operadores y priorízalos.


Utiliza cuantos paréntesis sean necesarios.

Puedes utilizar variables adicionales para acortar la expresión (sin embargo, no es muy
necesario). Prueba tu código cuidadosamente.

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:
y = -0.19258202567760344

x = float(input("Ingresa el valor para x: "))

# Escribe tu código aquí.

print("y =", y)

2.6.1.11 LABORATORIO: Operadores y expresiones

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 formato.
• 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
minutos de 0 a 59. El resultado debe ser mostrado en la consola.

Por ejemplo, si el evento comienza a las 12:17 y dura 59 minutos, terminará a las 13:16.

No te preocupes si tu código no es perfecto, está bien si acepta una hora invalida, lo más
importante es que el código produzca una salida correcta acorde a la entrada dada.

Prueba el código cuidadosamente. Pista: utilizar el operador % puede ser clave para el
éxito.

Datos de Prueba
Entrada de muestra:12
17
59

Salida esperada: 13:16

Entrada de muestra:23

58
642

Salida esperada: 10:40

Entrada de muestra:0

1
2939

Salida esperada: 1:0

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


mins = int(input("Minuto de inicio (minutos): "))
dura = int(input("Duración del evento (minutos): "))

# Escribe tu código aqui.

2.6.1.12 RESUMEN DE SECCIÓN

Puntos Clave

1. La función print() envía datos a la consola, mientras que la función input() obtiene
datos de la consola.

2. La función input() viene con un parámetro inicial: un mensaje de tipo cadena para el
usuario. Permite escribir un mensaje antes de la entrada del usuario, por ejemplo:

name = input("Ingresa tu nombre: ")

print("Hola, " + name + ". ¡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:

name = input("Ingresa tu nombre: ")


print("Hola, " + name + ". ¡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:

num_1 = input("Ingresa el primer número: ") # Ingresa 12


num_2 = input("Ingresa el segundo número: ") # Ingresa 21

print(num_1 + num_2) el programa retorna 1221

5. También se pueden multiplicar ( * - replicación) cadenas, por ejemplo:

my_input = input("Ingresa algo: ") # Ejemplo: hola


print(my_input * 3) # Salida esperada: holaholahola

Ejercicio 1

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

x = int(input("Ingresa un número: ")) # El usuario ingresa un 2


print(x * "5")

Revisar

55

Ejercicio 2

¿Cuál es la salida esperada del siguiente código

x = input("Ingresa un número: ") # El usuario ingresa un 2


print(type(x))
Revisar

<class 'str'>

2.6.1.13 Módulo Completado

¡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 formato 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.

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.

Fundamentos de Python 1: Módulo 3

Fundamentos de Python 1:
Módulo 3
En este módulo, aprenderás sobre:

• Datos de tipo booleano.


• Operadores relacionales.
• Cómo tomar decisiones en Python (if, if-else, if-elif, else).
• Cómo repetir la ejecución de código usando los bucles (while, for).
• Cómo realizar operaciones lógicas y de bit a bit en Python.
• Listas en Python (construcción, indexación y segmentación; manipulación de
contenido).
• Cómo ordenar una lista usando algoritmos de clasificación de burbujas.
• Listas multidimensionales y sus aplicaciones.
3.1.1.1 Tomando decisiones en Python

Preguntas y respuestas
Un programador escribe un programa y el programa hace preguntas.

Una computadora ejecuta el programa y proporciona las respuestas. El programa debe


ser capaz de reaccionar de acuerdo con las respuestas recibidas.

Afortunadamente, las computadoras solo conocen dos tipos de respuestas:

• Si, es cierto.
• No, esto es falso.

Nunca obtendrás una respuesta como Déjame pensar..., no lo sé, o probablemente sí,
pero no lo sé con seguridad.

Para hacer preguntas, Python utiliza un conjunto de operadores muy especiales.


Revisemos uno tras otro, ilustrando sus efectos en algunos ejemplos simples.

Comparación: operador de igualdad


Pregunta: ¿Son dos valores iguales?

Para hacer esta pregunta, se utiliza el == operador (igual igual).

No olvides esta importante distinción:

• = es un operador de asignación, por ejemplo, a = b assigna a la variable a el


valor de b .
• == es una pregunta ¿Son estos valores iguales? así que a == b compara a y b .

Es un operador binario con enlazado del lado izquierdo. 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 - por supuesto, 2 es igual a 2. Python responderá True (recuerda este par de
literales predefinidos, True y False - también son palabras clave reservadas de Python).
Pregunta #2: ¿Cuál es el resultado de la siguiente comparación?

2 == 2 Revisar

Esta pregunta no es tan fácil como la primera. Por suerte, Python es capaz de convertir el
valor entero en su equivalente real, y en consecuencia, la respuesta es True .

Pregunta #3: ¿Cuál es el resultado de la siguiente comparación?

1 == 2 Revisar

Esto debería ser fácil. La respuesta será (o mejor dicho, siempre es) False .

3.1.1.2 Tomando decisiones en Python

Igualdad: El operador igual a (==)


El operador == (igual a) compara los valores de dos operandos. Si son iguales, el
resultado de la comparación es True . Si no son iguales, el resultado de la comparación
es False .

Observa la comparación de igualdad a continuación: ¿Cuál es el resultado de esta


operación?

var == 0

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

black_sheep == 2 * white_sheep

Debido a la baja prioridad del operador == , la pregunta será tratada como la siguiente:

black_sheep == (2 * white_sheep)
3.1.1.3 Tomando decisiones en Python

Operadores de comparación: mayor que


También se puede hacer una pregunta de comparación usando el operador > (mayor
que).

Si deseas saber si hay más ovejas negras que blancas, puedes escribirlo de la siguiente
manera:

black_sheep > white_sheep # 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 del 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:

centigrade_outside ≥ 0.0 # mayor o igual que

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

Observa este ejemplo simple:

current_velocity_mph < 85 # Menor que

current_velocity_mph ≤ 85 # Menor o igual que

Vamos a comprobar si existe un riesgo de ser multados por la ley (la primera pregunta es
estricta, la segunda no).

El contenido de la variable te dirá la respuesta a la pregunta.


La segunda posibilidad es más conveniente y mucho más común: puedes utilizar la
respuesta que obtengas para tomar una decisión sobre el futuro del programa.

Necesitas una instrucción especial para este propósito, y la discutiremos muy pronto.

Ahora necesitamos actualizar nuestra tabla de prioridades, y poner todos los nuevos
operadores en ella. Ahora se ve como a continuación:

Prioridad Operador
1 +, - unario

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

5 <, <=, >, >=


6 ==, !=

3.1.1.4 LABORATORIO: Preguntas y respuestas

LABORATORIO

Tiempo Estimado
5 minutos

Nivel de Dificultad
Muy Fácil

Objetivos
• Familiarizarse con la función input() .
• Familiarizarse con los operadores de comparación en Python.

Escenario
Usando uno de los operadores de comparación en Python, escribe un programa simple
de dos líneas que tome el parámetro n como entrada, que es un entero, e
imprime False si n es menor que 100 , y True si n es mayor o igual que 100 .

No debes crear ningún bloque if (hablaremos de ellos muy pronto). Prueba tu código
usando los datos que te proporcionamos.
Datos de Prueba

Ejemplo de entrada: 55

Resultado esperado: False

Ejemplo de entrada: 99

Resultado esperado: False

Ejemplo de entrada: 100

Resultado esperado: True

Ejemplo de entrada: 101

Resultado esperado: True

Ejemplo de entrada: -5

Resultado esperado: False

Ejemplo de entrada: +123

Resultado esperado: True

3.1.1.5 Tomando decisiones en Python

Condiciones y ejecución condicional


Ya sabes como hacer preguntas a Python, pero aún no sabes como hacer un uso
razonable de las respuestas. Se debe tener un mecanismo que le permita hacer algo si se
cumple una condición, y no hacerlo si no se cumple.

Es como en la vida real: haces ciertas cosas o no cuando se cumple una condición
específica, por ejemplo, sales a caminar si el clima es bueno, o te quedas en casa si está
húmedo y frío.

Para tomar tales decisiones, Python ofrece una instrucción especial. Debido a su
naturaleza y su aplicación, se denomina instrucción condicional (o sentencia condicional).

Existen varias variantes de la misma. Comenzaremos con la más simple, aumentando la


dificultad lentamente.

La primera forma de una sentencia condicional, que puede ver a continuación, está
escrita de manera muy informal pero figurada:
if true_or_not:
do_this_if_true

Esta sentencia condicional consta de los siguientes elementos, estrictamente necesarios


en este orden:

• La palabra clave reservada if .


• Uno o más espacios en blanco.
• Una expresión (una pregunta o una respuesta) cuyo valor se interpretar
únicamente en términos de True (cuando su valor no sea cero) y False (cuando
sea igual a cero).
• Unos dos puntos seguido de una nueva línea.
• Una instrucción con sangría o un conjunto de instrucciones (se requiere
absolutamente al menos una instrucción); la sangría se puede lograr de dos
maneras: insertando un número particular de espacios (la recomendación es
usar cuatro espacios de sangría), o usando el tabulador; nota: si hay mas de una
instrucción en la parte con sangría, la sangría debe ser la misma en todas las
líneas; aunque puede parecer lo mismo si se mezclan tabuladores con espacios,
es importante que todas las sangrías sean exactamente iguales Python 3 no
permite mezclar espacios y tabuladores para la sangría.

¿Cómo funciona esta sentencia?

• Si la expresión true_or_not representa la verdad (es decir, su valor no es igual a


cero), las sentencias con sangría se ejecutarán.
• Si la expresión true_or_not no representa la verdad (es decir, su valor es igual a
cero), las sentencias con sangría se omitirán , 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:

si el clima es bueno, saldremos a caminar

después, almorzaremos

Como puedes ver, almorzar no es una actividad condicional y no depende del clima.

Sabiendo que condiciones influyen en nuestro comportamiento y asumiendo que


tenemos las funciones sin parámetros go_for_a_walk() y have_lunch() , podemos
escribir el siguiente fragmento de código:

if the_weather_is_good:
go_for_a_walk()
have_lunch()
3.1.1.6 Tomando decisiones en Python

Ejecución condicional: la sentencia 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 sleep_and_dream() , el código toma la siguiente forma:

if sheep_counter >= 120: # #evalúa una expresión condicional

sleep_and_dream() #se ejecuta si la expresión condicional es True

Puedes leerlo como sigue: si sheep_counter es mayor o igual que 120 , entonces duerme
y sueña (es decir, ejecuta la función sleep_and_dream ).

Hemos dicho que las sentencias condicionales deben tener sangría. Esto crea una
estructura muy legible, demostrando claramente todas las rutas de ejecución posibles en
el código.

Analiza el siguiente código:

if sheep_counter >= 120:


make_a_bed()
take_a_shower()
sleep_and_dream()
feed_the_sheepdogs()

Como puedes ver, tender la cama, tomar una ducha y dormir y soñar se
ejecutan condicionalmente, cuando sheep_counter alcanza el límite deseado.

Alimentar a los perros, sin embargo, siempre se hace (es decir, la


función feed_the_sheepdogs() 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 sentencia condicional, que también permite
realizar una acción adicional cuando no se cumple la condición.

Ejecución condicional: la sentencia if-else


Comenzamos con una frase simple que decía: Si el clima es bueno, saldremos a caminar.

Nota: no hay una palabra sobre lo que sucederá si el clima es malo. Solo sabemos que no
saldremos al aire libre, pero no sabemos que podríamos hacer. Es posible que también
queramos planificar algo en caso de mal tiempo.

Podemos decir, por ejemplo: Si el clima es bueno, saldremos a caminar, de lo contrario,


iremos al cine.
Ahora sabemos lo que haremos si se cumplen las condiciones, y sabemos lo que
haremos si no todo sale como queremos. En otras palabras, tenemos un "Plan B".

Python nos permite expresar dichos planes alternativos. Esto se hace con una segunda
forma, ligeramente mas compleja, de la sentencia condicional, la sentencia if-else:

if true_or_false_condition:
perform_if_condition_true
else:
perform_if_condition_false

Por lo tanto, hay una nueva palabra: else - esta es una palabra clave 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 True (su valor no es igual a cero), la


instrucción perform_if_condition_true se ejecuta, y la sentencia condicional
llega a su fin.
• Si la condición se evalúa como False (es igual a cero), la
instrucción perform_if_condition_false se ejecuta, y la sentencia condicional
llega a su fin.

3.1.1.7 Tomando decisiones en Python

La sentencia if-else: más sobre ejecución condicional


Al utilizar esta forma de sentencia condicional, podemos describir nuestros planes de la
siguiente manera:

if the_weather_is_good:
go_for_a_walk()
else:
go_to_a_theater()
have_lunch()

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 the_weather_is_good:
go_for_a_walk()
have_fun()
else:
go_to_a_theater()
enjoy_the_movie()
have_lunch()

Sentencias if-else anidadas


Ahora, analicemos dos casos especiales de la sentencia 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 siguiente:

if the_weather_is_good:
if nice_restaurant_is_found:
have_lunch()
else:
eat_a_sandwich()
else:
if tickets_are_available:
go_to_the_theater()
else:
go_shopping()

Aquí hay dos puntos importantes:

• Este uso de la sentencia if se conoce como anidamiento; recuerda que


cada else se refiere al if que se encuentra en el mismo nivel de sangría; se
necesita saber esto para determinar cómo se relacionan los if y los else .
• Considera como la sangría mejora la legibilidad y hace que el código sea más fácil
de entender y rastrear.

La sentencia elif
El segundo caso especial presenta otra nueva palabra clave de Python: elif. Como
probablemente sospechas, es una forma más corta de else-if.

elif se usa para verificar más de una condición, y para detener cuando se encuentra la
primera sentencia 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 reservada elif desempeña su función.

Escribamos el mismo escenario empleando Python:

if the_weather_is_good:
go_for_a_walk()
elif tickets_are_available:
go_to_the_theater()
elif table_is_available:
go_for_lunch()
else:
play_chess_at_home()

La forma de ensamblar las siguientes sentencias if-elif-else a veces se denomina cascada.

Observa de nuevo como la sangría mejora la legibilidad del código.

Se debe prestar atención adicional a este caso:

• No debes usar else sin un if precedente.


• else siempre es la última rama de la cascada, independientemente de si has
usado elif o no.
• else es una parte opcional de la cascada, y puede omitirse.
• Si hay una rama else en la cascada, solo se ejecuta una de todas las ramas.
• Si no hay una rama else , es posible que no se ejecute ninguna de las opciones
disponibles.

Esto puede sonar un poco desconcertante, pero ojalá que algunos ejemplos simples
ayuden a comprenderlo mejor.

3.1.1.8 Tomando decisiones en Python

Analizando ejemplos de código


Ahora te mostraremos algunos programas simples pero completos. No los explicaremos
a 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 de una
serie de números y lo imprimen.

Ejemplo 1:

Comenzaremos con el caso más simple: ¿Cómo identificar el mayor de los dos números?
#Se leen dos números
number1 = int(input("Ingresa el primer número: "))
number2 = int(input("Ingresa el segundo número: "))

# Elige el número más grande


if number1 > number2:
larger_number = number1
else:
larger_number = number2

# Imprime el resultado
print("El número más grande es:", larger_number)

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:

#Se leen dos números


number1 = int(input("Ingresa el primer número: "))
number2 = int(input("Ingresa el segundo número: "))

# Elige el número más grande


if number1 > number2: larger_number = number1
else: larger_number = number2

# Imprime el resultado
print("El número más grande es:", larger_number)

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:

# Se leen tres números


number1 = int(input("Ingresa el primer número: "))
number2 = int(input("Ingresa el segundo número: "))
number3 = int(input("Ingresa el tercer número: "))

# Asumimos temporalmente que el primer número


# es el más grande.
# Lo verificaremos pronto.
largest_number = number1

# 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 number2 > largest_number:
largest_number = number2

# 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 number3 > largest_number:
largest_number = number3

# Imprime el resultado.
print("El número más grande es:", largest_number)

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.

3.1.1.9 Tomando decisiones en Python

Pseudocódigo e introducción a los bucles (ciclos)


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 sentencias condicionales y doscientas invocaciones


de la función input() . Por suerte, no necesitas lidiar con eso. Hay un enfoque más
simple.

Por ahora ignoraremos los requisitos de la sintaxis de Python e intentaremos analizar el


problema sin pensar en la programación real. En otras palabras, intentaremos escribir
el algoritmo, y cuando estemos contentos con él, lo implementaremos.

En este caso, utilizaremos un tipo de notación que no es un lenguaje de programación


real (no se puede compilar ni ejecutar), pero está formalizado, es conciso y se puede leer.
Se llama pseudocódigo.

Veamos nuestro pseudocódigo a continuación:

largest_number = -999999999
number = int(input())
if number == -1:
print(largest_number)
exit()
if number > largest_number:
largest_number = number
# Ir a la línea 02

¿Qué está pasando en él?

En primer lugar, podemos simplificar el programa si, al principio del código, le asignamos
a la variable largest_number un valor que será más pequeño que cualquiera de los
números ingresados. Usaremos -999999999 para ese propósito.

En segundo lugar, asumimos que nuestro algoritmo no sabrá por adelantado cuántos
números se entregarán al programa. Esperamos que el usuario ingrese todos los
números que desee; el algoritmo funcionará bien con cien y con mil números. ¿Cómo
hacemos eso?

Hacemos un trato con el usuario: cuando se ingresa el valor -1 , será una señal de que no
hay más datos y que el programa debe finalizar su trabajo.
De lo contrario, si el valor ingresado no es igual a -1 , el programa leerá otro número, y
así sucesivamente.

El truco se basa en la suposición de que cualquier parte del código se puede realizar más
de una vez, precisamente, tantas veces como sea necesario.

La ejecución de una determinada parte del código más de una vez se denomina bucle. El
significado de este término es probablemente obvio para ti.

Las líneas 02 a 08 forman un bucle. Los pasaremos tantas veces como sea
necesario para revisar todos los valores ingresados.

¿Puedes usar una estructura similar en un programa escrito en Python? Si, si puedes.

Información Adicional

Python a menudo viene con muchas funciones integradas que harán el trabajo por ti. Por
ejemplo, para encontrar el número más grande de todos, puede usar una función
incorporada de Python llamada max() . Puedes usarlo con múltiples argumentos. Analiza
el código de abajo:

# Se leen tres números.


number1 = int(input("Ingresa el primer número: "))
number2 = int(input("Ingresa el segundo número: "))
number3 = int(input("Ingresa el tercer número: "))

# Verifica cuál de los números es el mayor


# y pásalo a la variable largest_number

largest_number = max(number1, number2, number3)

# Imprime el resultado.
print("El número más grande es:", largest_number)

De la misma manera, puedes usar la función min() para devolver el número más
pequeño. Puedes reconstruir el código anterior y experimentar con él en Sandbox.

Vamos a hablar sobre estas (y muchas otras) funciones pronto. Por el momento, nuestro
enfoque se centrará en la ejecución condicional y los bucles para permitirte ganar más
confianza en la programación y enseñarte las habilidades que te permitirán comprender
y aplicar los dos conceptos en tu codigo. Entonces, por ahora, no estamos tomando
atajos.

3.1.1.10 LABORATORIO: Operadores de comparación y ejecución


condicional
LABORATORIO

Tiempo Estimado
5-10 minutos

Nivel de Dificultad
Fácil

Objetivos
• Familiarizarse con la función input().
• Familiarizarse con los operadores de comparación en Python.
• Familiarizarse con el concepto de ejecución condicional.

Escenario
Espatifilo, más comúnmente conocida como la planta de cuna de Moisés o flor de la paz,
es una de las plantas para interiores más populares que filtra las toxinas dañinas del aire.
Algunas de las toxinas que neutraliza incluyen benceno, formaldehído y amoníaco.

Imagina que tu programa de computadora ama estas plantas. Cada vez que recibe una
entrada en forma de la palabra Espatifilo , grita involuntariamente a la consola la
siguiente cadena: "¡Espatifilo es la mejor planta de todas!"

Escribe un programa que utilice el concepto de ejecución condicional, tome una cadena
como entrada y que:

• Imprima el enunciado "Si, ¡El ESPATIFILIO! es la mejor planta de


todos los tiempos!" en la pantalla si la cadena ingresada es "ESPATIFILIO" .
• Imprima "No, ¡quiero un gran ESPATIFILIO!" si la cadena ingresada
es "espatifilo".
• Imprima "¡ESPATIFILIO!, ¡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 ESPATIFILIO


también!

Datos de Prueba
Entrada de muestra: espatifilo

Resultado esperado: No, ¡quiero un gran ESPATIFILIO!


Entrada de ejemplo: pelargonio

Resultado esperado: !ESPATIFILIO!, ¡No pelargonio!

Entrada de muestra: ESPATIFILIO

Resultado esperado: Si, ¡El ESPATIFILIO es la mejor planta de todos los


tiempos!

3.1.1.11 LABORATORIO: Fundamentos de la sentencia if-else

LABORATORIO

Tiempo Estimado
10-15 minutos

Nivel de Dificultad
Fácil/Medio

Objetivos
Familiarizar al estudiante con:

• Utilizar la sentencia 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 de leche y miel, habitada por gente feliz y próspera. La gente
pagaba impuestos, por supuesto, su felicidad tenía límites. El impuesto más importante,
denominado Impuesto Personal de Ingresos (IPI, para abreviar) tenía que pagarse una
vez al año y se evaluó utilizando la siguiente regla:

• Si el ingreso del ciudadano no era superior a 85,528 pesos, el impuesto era igual al
18% del ingreso menos 556 pesos y 2 centavos (esta fue la llamada exención
fiscal ).
• Si el ingreso era superior a esta cantidad, el impuesto era igual a 14,839 pesos y 2
centavos, más el 32% del excedente sobre 85,528 pesos.

Tu tarea es escribir una calculadora de impuestos.

• Debe aceptar un valor de punto flotante: el ingreso.


• A continuación, debe imprimir el impuesto calculado, redondeado a pesos totales.
Hay una función llamada round() que hará el redondeo por ti, la encontrarás en
el código de esqueleto del editor.

Nota: Este país feliz nunca devuelve dinero a sus ciudadanos. Si el impuesto calculado es
menor que cero, solo significa que no hay impuesto (el impuesto es igual a cero). Ten esto
en cuenta durante tus cálculos.

Observa el código en el editor: solo lee un valor de entrada y genera un resultado, por lo
que debes completarlo con algunos cálculos inteligentes.

Prueba tu código con los datos que hemos proporcionado.

Datos de Prueba
Entrada de muestra: 10000
Resultado esperado: El impuesto es: 1244.0 pesos

Entrada de muestra: 100000


Resultado esperado: El impuesto es: 19470.0 pesos

Entrada de muestra: 1000


Resultado esperado: El impuesto es: 0.0 pesos

Entrada de muestra: -100


Resultado esperado: El impuesto es: 0.0 pesos

income = float(input("Introduce el ingreso anual:"))

#
# Escribe tu código aquí.
#

tax = round(tax, 0)
print("El impuesto es:", tax, "pesos")

3.1.1.12 LABORATORIO: Fundamentos de la sentencia if-elif-else

LABORATORIO

Tiempo Estimado
10-15 minutos
Nivel de Dificultad
Fácil/Medio

Objetivos
Familiarizar al estudiante con:

• Utilizar la sentencia if-elif-else.


• Encontrar la implementación adecuada de las reglas definidas verbalmente.
• Emplear el código de prueba empleando entradas y salidas de muestra.

Escenario
Como seguramente sabrás, debido a algunas razones astronómicas, el año pueden
ser bisiesto o común. Los primeros tienen una duración de 366 días, mientras que los
últimos tienen una duración de 365 días.

Desde la introducción del calendario Gregoriano (en 1582), se utiliza la siguiente regla
para determinar el tipo de año:

• Si el número del año no es divisible entre cuatro, es un año común.


• De lo contrario, si el número del año no es divisible entre 100, es un año bisiesto.
• De lo contrario, si el número del año no es divisible entre 400, es un año común.
• De lo contrario, es un año bisiesto.

Observa el código en el editor: solo lee un número de año y debe completarse con las
instrucciones que implementan la prueba que acabamos de describir.

El código debe mostrar uno de los dos mensajes posibles, que son Año Bisiesto o Año
Común , según el valor ingresado.

Sería bueno verificar si el año ingresado cae en la era Gregoriana y emitir una advertencia
de lo contrario: No dentro del período del calendario Gregoriano . Consejo:
utiliza los operadores != y % .

Prueba tu código con los datos que hemos proporcionado.

Datos de Prueba
Entrada de muestra: 2000
Resultado esperado: Año Bisiesto

Entrada de muestra: 2015


Resultado esperado: Año Común

Entrada de muestra: 1999


Resultado esperado: Año Común
Entrada de muestra: 1996
Resultado esperado: Año Bisiesto

Entrada de muestra: 1580


Resultado esperado: No esta dentro del período del calendario Gregoriano

year = int(input("Introduce un año:"))

#
# Escribe tu código aquí.
#

3.1.1.13 RESUMEN DE LA SECCIÓN (1/2)

Puntos Clave
1. Los operadores de comparación (o también denominados operadores 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


x == y # False
Devuelve si los valores de los operandos son iguales,
== y False de lo contrario.
x == z # True

x != y # True
Devuelve True si los valores de los operandos no
!= son iguales, y False de lo contrario.
x != z # False

Devuelve True 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
< menor que el valor del operando derecho, y < z # False
y False de lo contrario.
x >= y # False
Devuelve True si el valor del operando izquierdo es
x >= z # True
≥ mayor o igual al valor del operando derecho,
y >= z # True
y False de lo contrario.
x <= y # True
Devuelve True si el valor del operando izquierdo es
x <= z # True
≤ menor o igual al valor del operando derecho,
y <= z # False
y False de lo contrario.

2. Cuando deseas ejecutar algún código solo si se cumple una determinada condición,
puedes usar una sentencia condicional:
• Una única sentencia 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 sentencias if , por ejemplo:

x = 10

if x > 5: # primera condición


print("x es mayor que 5") # Ejecutado si la primera
condición es Verdadera.

if x < 10: # segunda condición


print("x is less than 10") # Ejecutado si la segunda
condición es Verdadera.

if x == 10: # tercera condición


print("x is equal to 10") # Ejecutado si la tercera
condición es Verdadera.

Cada sentencia if se prueba por separado.

• Una sentencia de if-else , por ejemplo:

x = 10

if x < 10: # Condición


print("x es menor que 10") # Ejecutado si la condición es
Verdadera.

else:
print("x es mayor o igual a 10") # Ejecutado si la
condición es Falsa.

• Una serie de sentencias if seguidas de un else , por ejemplo:

x = 10

if x > 5: # True
print("x > 5")

if x > 8: # True
print("x > 8")

if x > 10: # False


print("x > 10")
else:
print("se ejecutará el else")

Cada if se prueba por separado. El cuerpo de else se ejecuta si el último if es False .


• La sentencia if-elif-else , por ejemplo:

x = 10

if x == 10: # True
print("x == 10")

if x > 15: # False


print("x > 15")

elif x > 10: # False


print("x > 10")

elif x > 5: # True


print("x > 5")

else:
print("else no será ejecutado")

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 .

• Sentencias condicionales anidadas, ejemplo:

x = 10

if x > 5: # True
if x == 6: # False
print("anidado: x == 6")
elif x == 10: # True
print("anidado: x == 10")
else:
print("anidado: else")
else:
print("else")

3.1.1.14 RESUMEN DE LA SECCIÓN (2/2)

Puntos Clave: continuación

Ejercicio 1

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


x = 5
y = 10
z = 8

print(x > y)
print(y > z)

Revisar

False
True
salida

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
salida

Ejercicio 3
¿Cuál es la salida del siguiente fragmento de código?
x, y, z = 5, 10, 8
x, y, z = z, y, x

print(x > z)
print((y - 5) == x)

Revisar

True
False
salida

Ejercicio 4
¿Cuál es la salida del siguiente fragmento de código?
x = 10

if x == 10:
print(x == 10)
if x > 5:
print(x > 5)
if x < 10:
print(x < 10)
else:
print("else")
Revisar

True
True
else
salida

3.2.1.1 Bucles en Python | while

Bucles (ciclos) en el código con while


¿Estás de acuerdo con la sentencia presentada a continuación?

mientras haya algo que hacer


hazlo

Toma en cuenta que este registro también declara que, si no hay nada que hacer, nada
ocurrirá.

En general, en Python, un bucle se puede representar de la siguiente manera:

while conditional_expression:
instruction

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


sentencias 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 conditional_expression:
instruction_one
instruction_two
instruction_three
:
:
instruction_n
Ahora, es importante recordar que:

• Si deseas ejecutar más de una sentencia 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 bucle.
• Si la condición es False (igual a cero) tan pronto como se compruebe por primera
vez, el cuerpo no se ejecuta ni una sola vez (ten en cuenta la analogía de no tener
que hacer nada si no hay nada que hacer).
• El cuerpo debe poder cambiar el valor de la condición, porque si la condición
es True al principio, el cuerpo podría funcionar continuamente hasta el infinito.
Observa que hacer una cosa generalmente disminuye la cantidad de cosas por
hacer.

Un bucle infinito
Un bucle infinito, también denominado bucle sin fin, es una secuencia de instrucciones
en un programa que se repite indefinidamente (bucle sin fin).

Este es un ejemplo de un bucle que no puede finalizar su ejecución:

while True:
print("Estoy atrapado dentro de un bucle.")

Este bucle imprimirá infinitamente "Estoy atrapado dentro de un bucle". En la


pantalla.

NOTA

Si deseas obtener la mejor experiencia de aprendizaje al ver cómo se comporta un bucle


infinito, inicia IDLE, crea 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 bucle". 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 bucle. Hablaremos de ello más adelante en el curso.

Volvamos al bosquejo del algoritmo que te mostramos recientemente. Te mostraremos


como usar este bucle recién aprendido para encontrar el número más grande de un gran
conjunto de datos ingresados.

Analiza el programa cuidadosamente. Localiza donde comienza el bucle (línea 8) y


descubre como se sale del cuerpo del bucle:

# Almacena el actual número más grande aquí.


largest_number = -999999999
# Ingresa el primer valor.
number = int(input("Introduce un número o escribe -1 para detener: "))

# Si el número no es igual a -1, continuaremos


while number != -1:
# ¿Es el número más grande que el valor de largest_number?
if number > largest_number:
# Sí si, se actualiza largest_number.
largest_number = number
# Ingresa el siguiente número.
number = int(input("Introduce un número o escribe -1 para detener: "))

# Imprime el número más grande


print("El número más grande es:", largest_number)

Comprueba como este código implementa el algoritmo que te mostramos anteriormente.

3.2.1.2 Bucles en Python | while

El bucle while: más ejemplos


Veamos otro ejemplo utilizando el bucle while . Sigue los comentarios para descubrir la
idea y la solución.

# Un programa que lee una secuencia de números


# y cuenta cuántos números son pares y cuántos son impares.
# El programa termina cuando se ingresa un cero.

odd_numbers = 0
even_numbers = 0

# Lee el primer número.


number = int(input("Introduce un número o escribe 0 para detener: "))

# 0 termina la ejecución.
while number != 0:
# Verificar si el número es impar.
if number % 2 == 1:
# Incrementar el contador de números impares odd_numbers.
odd_numbers += 1
else:
# Incrementar el contador de números pares even_numbers.
even_numbers += 1
# Leer el siguiente número.
number = int(input("Introduce un número o escribe 0 para detener:
"))

# Imprimir resultados.
print("Cuenta de números impares:", odd_numbers)
print("Cuenta de números pares:", even_numbers)
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 number != 0: y while number:

La condición que verifica si un número es impar también puede codificarse en estas


formas equivalentes:

if number % 2 == 1: y if number % 2:

Empleando una variable counter para salir del bucle


Observa el fragmento de código a continuación:

counter = 5
while counter != 0:
print("Dentro del bucle.", counter)
counter -= 1
print("Fuera del bucle.", counter)

Este código está destinado a imprimir la cadena "Dentro del bucle." y el valor
almacenado en la variable counter durante un bucle dado exactamente cinco veces. Una
vez que la condición se haya cumplido (la variable counter ha alcanzado 0 ), se sale del
bucle y aparece el mensaje "Fuera del bucle". así como tambien el valor almacenado
en counter se imprime.

Pero hay una cosa que se puede escribir de forma más compacta: la condición del
bucle while .

¿Puedes ver la diferencia?

counter = 5
while counter:
print("Dentro del bucle.", counter)
counter -= 1
print("Fuera del bucle.", counter)

¿Es más compacto que antes? Un poco. ¿Es más legible? Eso es discutible.

RECUERDA

No te sientas obligado a codificar tus programas de una manera que siempre sea la más
corta y la más compacta. La legibilidad puede ser un factor más importante. Manten tu
código listo para un nuevo programador.
secret_number = 777

print(
"""
+==================================+
| ¡Bienvenido a mi juego, muggle! |
| Introduce un número entero |
| y adivina qué número he |
| elegido para ti. |
| Entonces, |
| ¿Cuál es el número secreto? |
+==================================+
""")

3.2.1.4 Bucles en Python | for

Bucles en tu código con for


Otro tipo de bucle disponible en Python proviene de la observación de que a veces es
más importante contar los "giros o vueltas" del bucle que verificar las condiciones.

Imagina que el cuerpo de un bucle debe ejecutarse exactamente cien veces. Si deseas
utilizar el bucle while para hacerlo, puede tener este aspecto:

i = 0
while i < 100:
# do_something()
i += 1

Sería bueno si alguien pudiera hacer esta cuenta aburrida por ti. ¿Es eso posible?

Por supuesto que lo es, hay un bucle especial para este tipo de tareas, y se llama for .

En realidad, el bucle 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):
# do_something()
pass

Existen algunos elementos nuevos. Déjanos contarte sobre ellos:


• La palabra reservada for abre el bucle for ; nota - No hay condición después de
eso; no tienes que pensar en las condiciones, ya que se verifican internamente, sin
ninguna intervención.
• Cualquier variable después de la palabra reservada for es la variable de
control del bucle; cuenta los giros del bucle 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 bucle 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 bucle - no hace nada en absoluto;
es una instrucción vacía : la colocamos aquí porque la sintaxis del bucle 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 bucle.

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 bucle se ha ejecutado diez veces (es el argumento de la función range() ).
• El valor de la última variable de control es 9 (no 10 , ya que comienza desde 0 , no
desde 1 ).

La invocación de la función range() puede estar equipada con dos argumentos, no solo
uno:

for i in range(2, 8):


print("El valor de i es actualmente", i)

En este caso, el primer argumento determina el valor inicial (primero) de la variable de


control.

El último argumento muestra el primer valor que no se asignará a la variable de control.

Nota: la función range() solo acepta enteros como argumentos y genera secuencias de
enteros.
¿Puedes adivinar la salida del programa? Ejecútalo para comprobar si ahora también
estabas en lo cierto.

El primer valor mostrado es 2 (tomado del primer argumento de range() ).

El último es 7 (aunque el segundo argumento de range() es 8 ).

3.2.1.5 Bucles en Python | for

Más sobre el bucle 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 bucle (como puedes sospechar, el valor predeterminado del incremento es
1).

¿Puedes decirnos cuántas líneas aparecerán en la consola y qué valores contendrán?

Ejecuta el programa para averiguar si tenías razón.

Deberías poder ver las siguientes líneas en la ventana de la consola:

El valor de i es actualmente 2
El valor de i es actualmente 5
salida

¿Sabes por qué? El primer argumento pasado a la función range() nos dice cual es el
número de inicio de la secuencia (por lo tanto, 2 en la salida). El segundo argumento le
dice a la función dónde detener la secuencia (la función genera números hasta el número
indicado por el segundo argumento, pero no lo incluye). Finalmente, el tercer argumento
indica el paso, que en realidad significa la diferencia entre cada número en la secuencia
de números generados por la función.

2 (número inicial) → 5 ( 2 incremento por 3 es igual a 5 - el número está dentro del rango
de 2 a 8) → 8 ( 5 incremento por 3 es igual a 8 - el número no está dentro del rango de 2
a 8, porque el parámetro de parada no está incluido en la secuencia de números
generados por la función).

Nota: si el conjunto generado por la función range() está vacío, el bucle no ejecutará su
cuerpo en absoluto.

Al igual que aquí, no habrá salida:

for i in range(1, 1):


print("El valor de i es actualmente", i)

Nota: el conjunto generado por range() debe ordenarse en un orden ascendente. No


hay forma de forzar el range() para crear un conjunto en una forma diferente. Esto
significa que el segundo argumento de range() debe ser mayor que el primero.

Por lo tanto, tampoco habrá salida aquí:

for i in range(2, 1):


print("El valor de i es actualmente", i)

Echemos un vistazo a un programa corto cuya tarea es escribir algunas de las primeras
potencias de dos:

power = 1
for expo in range(16):
print("2 a la potencia de", expo, "es", power)
power *= 2

La variable expo se utiliza como una variable de control para el bucle 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)

3.2.1.6 LABORATORIO: Fundamentos del bucle for: el conteo

LABORATORIO

Tiempo Estimado

5 minutos

Nivel de dificultad

Muy fácil

Objetivos
Familiarizar al estudiante con:
• Utilizar el bucle for.
• Reflejar situaciones de la vida real en código de computadora.

Escenario
¿Sabes lo que es Mississippi? Bueno, es el nombre de uno de los estados y ríos en los
Estados Unidos. El río Mississippi tiene aproximadamente 2,340 millas de largo, lo que lo
convierte en el segundo río más largo de los Estados Unidos (el más largo es el río
Missouri). ¡Es tan largo que una sola gota de agua necesita 90 días para recorrer toda su
longitud!

La palabra Mississippi también se usa para un propósito ligeramente diferente:


para contar mississippily (mississippimente).

Si no estás familiarizado con la frase, estamos aquí para explicarte lo que significa: se
utiliza para contar segundos.

La idea detrás de esto es que agregar la palabra Mississippi a un número al contar los
segundos en voz alta hace que suene más cercano al reloj, y por lo tanto "un Mississippi,
dos Mississippi, tres Mississippi" tomará aproximadamente unos tres segundos reales de
tiempo. A menudo lo usan los niños que juegan al escondite para asegurarse de que el
buscador haga un conteo honesto.

Tu tarea es muy simple aquí: escribe un programa que use un bucle for para "contar de
forma mississippi" hasta cinco. Habiendo contado hasta cinco, el programa debería
imprimir en la pantalla el mensaje final "¡Listos 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 sentencia 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 bucle for durante un segundo, de modo que el
mensaje enviado a la consola se parezca a un conteo real. No te preocupes, pronto
aprenderás más sobre módulos y métodos.

Salida esperada
1 Mississippi
2 Mississippi
3 Mississippi
4 Mississippi
5 Mississippi
import time

# Escribe un bucle for que cuente hasta cinco.


# Cuerpo del bucle: imprime el número de iteración del bucle
# y la palabra "Mississippi".
# Cuerpo del bucle - usar: time.sleep (1)

# Escribe una función de impresión con el mensaje final.

3.2.1.7 Control de bucles en Python | break y continue

Las sentencias break y continue


Hasta ahora, hemos tratado el cuerpo del bucle como una secuencia indivisible e
inseparable de instrucciones que se realizan completamente en cada giro del bucle. Sin
embargo, como desarrollador, podrías enfrentar las siguientes opciones:

• Parece que no es necesario continuar el bucle en su totalidad; se debe abstener


de seguir ejecutando el cuerpo del bucle e ir más allá.
• Parece que necesitas comenzar el siguiente giro del bucle 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 bucle inmediatamente, e incondicionalmente termina la operación


del bucle; el programa comienza a ejecutar la instrucción más cercana después
del cuerpo del bucle.
• continue : se comporta como si el programa hubiera llegado repentinamente al
final del cuerpo; el siguiente turno se inicia y la expresión de condición se prueba
de inmediato.

Ambas palabras son palabras clave reservadas.

Ahora te mostraremos dos ejemplos simples para ilustrar como funcionan las dos
instrucciones. Mira el código en el editor. Ejecuta el programa y analiza la salida. Modifica
el código y experimenta.

# break - ejemplo

print("La instrucción break:")


for i in range(1, 6):
if i == 3:
break
print("Dentro del bucle.", i)
print("Fuera del bucle.")

# continue - ejemplo

print("\nLa instrucción continue:")


for i in range(1, 6):
if i == 3:
continue
print("Dentro del bucle.", i)
print("Fuera del bucle.")

3.2.1.8 Control de bucles en Python | break y continue

Las sentencias break y continue: más ejemplos


Regresemos a nuestro programa que reconoce el más grande entre los números
ingresados. Lo convertiremos dos veces, usando las instrucciones de break y continue .

Analiza el código y determina como las usarías.

La variante empleando break es la siguiente:

largest_number = -99999999
counter = 0

while True:
number = int(input("Ingresa un número o escribe -1 para finalizar el programa: "))
if number == -1:
break
counter += 1
if number > largest_number:
largest_number = number

if counter != 0:
print("El número más grande es", largest_number)
else:
print("No has ingresado ningún número.")

Ejecútalo, pruébalo y experimenta con él.

Y ahora la variante con continue :


largest_number = -99999999
counter = 0
number = int(input("Ingresa un número o escribe -1 para finalizar el programa: "))

while number != -1:


if number == -1:
continue
counter += 1

if number > largest_number:


largest_number = number
number = int(input("Ingresa un número o escribe -1 para finalizar el programa: "))

if counter:
print("El número más grande es", largest_number)
else:
print("No has ingresado ningún número.")

Observa con atención, el usuario ingresa el primer número antes de que el programa
ingrese al bucle while . El número siguiente se ingresa cuando el programa ya está en el
bucle.
De nuevo: ejecútalo, pruébalo y experimenta con él.

3.2.1.9 LABORATORIO: La sentencia break - Atascado en un bucle

LABORATORIO

Tiempo Estimado
10 minutos

Nivel de Dificultad
Fácil

Objetivos
Familiarizar al estudiante con:

• Utilizar la instrucción break en los bucles.


• Reflejar situaciones de la vida real en código de computadora.

Escenario
La instrucción break se implementa para salir/terminar un bucle.
Diseña un programa que use un bucle 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 bucle con éxito". Debe
imprimirse en la pantalla y el bucle debe terminar.

No imprimas ninguna de las palabras ingresadas por el usuario. Utiliza el concepto de


ejecución condicional y la sentencia break .

3.2.1.10 LABORATORIO: La sentencia continue - El Feo Devorador


de Vocales

LABORATORIO

Tiempo Estimado
10-15 minutos

Nivel de Dificultad
Fácil

Objectives
Familiarizar al estudiante con:

• Utilizar la instrucción continue en los bucles.


• 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 sentencias dentro del bucle.

Se puede usar tanto con while y for.

Tu tarea aquí es muy especial: ¡Debes diseñar un devorador de vocales! Escribe un


programa que use:

• Un bucle for .
• El concepto de ejecución condicional (if-elif-else).
• La sentencia continue .

Tu programa debe:

• Pedir al usuario que ingrese una palabra.


• Utiliza user_word = user_word.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:

G
R
G
R
Y

Entrada de muestra: abstemious

Salida esperada:

B
S
T
M
S

Entrada de muestra: IOUEA

Salida esperada:

(vacio)

# Indicar al usuario que ingrese una palabra


# y asignarlo a la variable user_word.

for letter in user_word:


# Completa el cuerpo del bucle for.

3.2.1.11 LABORATORIO: La sentencia continue - El Bonito Devorador


de Vocales
LABORATORIO

Tiempo Estimado
5-10 minutos

Nivel de Dificultad
Fácil

Objetivos
Familiarizar al estudiante con:

• Utilizar la instrucción continue en los bucles.


• 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 bucle for .
• El concepto de ejecución condicional (if-elif-else).
• La instrucción continue .

Tu programa debe:

• Pedir al usuario que ingrese una palabra.


• Utilizar user_word = user_word.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.
• Emplea 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 word_without_vowels e imprime la
variable en la pantalla.

Analiza el código en el editor. Hemos creado word_without_vowels 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
bucle, y asignarlo a la variable word_without_vowels .

Prueba tu programa con los datos que le proporcionamos.


Datos de Prueba
Entrada de muestra: Gregory

Salida esperada:

GRGRY

Entrada de muestra: abstemious

Salida esperada:

BSTMS

Entrada de muestra: IOUEA

Salida esperada:

(vacio)

word_without_vowels = ""

# Indicar al usuario que ingrese una palabra


# y asignarla a la variable user_word.

for letter in user_word:


# Completa el cuerpo del bucle.

# Imprimir la palabra asignada a word_without_vowels.

3.2.1.12 Bucles en Python | else

El bucle while y la rama else


Ambos bucles while y for , tienen una característica interesante (y rara vez se usa).

Te mostraremos como 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


reservada else .

Como pudiste haber sospechado, los bucles también pueden tener la rama else , como
los if .
La rama else del bucle siempre se ejecuta una vez, independientemente de si el bucle 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 bucle no tenga oportunidad de ejecutar su


cuerpo ni una sola vez:

i = 5
while i < 5:
print(i)
i += 1
else:
print("else:", i)

El estado del while es False al principio, ¿puedes verlo?

Ejecuta y prueba el programa, y verifica si se ha ejecutado o no la rama else .

i = 1
while i < 5:
print(i)
i += 1
else:
print("else:", i)

3.2.1.13 Bucles en Python | else

El bucle for y la rama else


Los bucles for se comportan de manera un poco diferente: echa un vistazo al fragmento
en el editor y ejecútalo.

La salida puede ser un poco sorprendente.

La variable i conserva su último valor.

Modifica el código un poco para realizar un experimento más.

i = 111
for i in range(2, 1):
print(i)
else:
print("else:", i)
¿Puedes adivinar la salida?

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


variable i antes del bucle.

Ejecuta el programa y verifica su salida.

Cuando el cuerpo del bucle no se ejecuta, la variable de control conserva el valor que
tenía antes del bucle.

Nota: si la variable de control no existe antes de que comience el bucle, 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)

3.2.1.14 LABORATORIO: Fundamentos del bucle while

LABORATORIO

Tiempo Estimado
20-30 minutos

Nivel de Dificultad
Medio

Objetivos
Familiarizar al estudiante con:

• Utilizar el bucle while .


• Encontrar la implementación adecuada de reglas definidas verbalmente.
• Reflejar situaciones de la vida real en código de computadora.
Escenario
Escucha esta historia: Un niño y su padre, un programador de computadoras, juegan con
bloques de madera. Están construyendo una pirámide.

Su pirámide es un poco rara, ya que en realidad es una pared en forma de pirámide, es


plana. La pirámide se apila de acuerdo con un principio simple: cada capa inferior
contiene un bloque más que la capa superior.

La figura ilustra la regla utilizada por los constructores:

Tu tarea es escribir un programa que lea la cantidad de bloques que tienen los
constructores, y generar la altura de la pirámide que se puede construir utilizando estos
bloques.

Nota: La altura se mide por el número de capas completas: si los constructores no tienen
la cantidad suficiente de bloques y no pueden completar la siguiente capa, terminan su
trabajo inmediatamente.

Prueba tu código con los datos que hemos proporcionado.

Datos de Prueba
Entrada de muestra: 6
Salida esperada: 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


Salida esperada: La altura de la pirámide es: 44

Entrada de muestra: 2
Salida esperada: La altura de la pirámide es: 1

blocks = int(input("Ingresa el número de bloques: "))


#
# Escribe tu código aquí.
#

print("La altura de la pirámide:", height)

3.2.1.15 LABORATORIO: Hipótesis de Collatz


LABORATORIO

Tiempo Estimado
20 minutos

Nivel de Dificultad
Media

Objetivos
Familiarizar al estudiante con:
• Utilizar el bucle while .
• Convertir bucles definidos verbalmente en código real de Python.

Escenario
En 1937, un matemático alemán llamado Lothar Collatz formuló una hipótesis intrigante
(aún no se ha comprobado) que se puede describir de la siguiente manera:

1. Toma cualquier número entero que no sea negativo y que no sea cero y asígnale
el nombre c0 .
2. Si es par, evalúa un nuevo c0 como c0 ÷ 2 .
3. De lo contrario, si es impar, evalúe un nuevo c0 como 3 × c0 + 1 .
4. Si c0 ≠ 1 , salta al punto 2.

La hipótesis dice que, independientemente del valor inicial de c0 , el valor siempre tiende
a 1.

Por supuesto, es una tarea extremadamente compleja usar una computadora para
probar la hipótesis de cualquier número natural (incluso puede requerir inteligencia
artificial), pero puede usar Python para verificar algunos números individuales. Tal vez
incluso encuentres el que refutaría la hipótesis.
Escribe un programa que lea un número natural y ejecute los pasos anteriores siempre
que c0 sea diferente de 1. También queremos que cuente los pasos necesarios para
lograr el objetivo. Tu código también debe mostrar todos los valores intermedios de c0 .

Sugerencia: la parte más importante del problema es como transformar la idea de Collatz
en un bucle while - esta es la clave del éxito.

Prueba tu código con los datos que hemos proporcionado.

Test Data
Entrada de muestra: 15
Salida esperada:
46
23
70
35
106
53
160
80
40
20
10
5
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
1535
4606
2303
6910
3455
10366
5183
15550
7775
23326
11663
34990
17495
52486
26243
78730
39365
118096
59048
29524
14762
7381
22144
11072
5536
2768
1384
692
346
173
520
260
130
65
196
98
49
148
74
37
112
56
28
14
7
22
11
34
17
52
26
13
40
20
10
5
16
8
4
2
1
pasos = 62

3.2.1.16 RESUMEN DE LA SECCIÓN (1/2)

Puntos Clave

1. Existen dos tipos de bucles en Python: while y for :

• El bucle while ejecuta una sentencia o un conjunto de sentencias siempre que


una condición booleana especificada sea verdadera, por ejemplo:

# Ejemplo 1
while True:
print("Atascado en un bucle infinito.")

# Ejemplo 2
counter = 5
while counter > 2:
print(counter)
counter -= 1

• El bucle 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 bucle for para iterar sobre una secuencia de
números usando la función incorporada range . Mira los ejemplos a continuación:

# Ejemplo 1
word = "Python"
for letter in word:
print(letter, end="*")

# Ejemplo 2
for i in range(1, 10):
if i % 2 == 0:
print(i)

2. Puedes usar las sentencias break y continue para cambiar el flujo de un bucle:

• Utiliza break para salir de un bucle, por ejemplo:


text = "OpenEDG Python Institute"
for letter in text:
if letter == "P":
break
print(letter, end="")

• Utiliza continue para omitir la iteración actual, y continuar con la siguiente


iteración, por ejemplo:

text = "pyxpyxpyx"
for letter in text:
if letter == "x":
continue
print(letter, end="")

3. Los bucles while y for también pueden tener una cláusula else en Python. La
cláusula else se ejecuta después de que el bucle finalice su ejecución siempre y cuando
no haya terminado con break , por ejemplo:

n = 0

while n != 3:
print(n)
n += 1
else:
print(n, "else")

print()

for i in range(0, 3):


print(i)
else:
print(i, "else")

4. La función range() genera una secuencia de números. Acepta enteros y devuelve


objetos de rango. La sintaxis de range() tiene el siguiente aspecto: range(start,
stop, step) , donde:

• start es un parámetro opcional que especifica el número de inicio de la


secuencia (0 por defecto)
• stop es un parámetro opcional que especifica el final de la secuencia generada
(no está incluido).
• y step es un parámetro opcional que especifica la diferencia entre los números
en la secuencia es (1 por defecto.)

Código de ejemplo:
for i in range(3):
print(i, end=" ") # Salidas: 0 1 2

for i in range(6, 1, -2):


print(i, end=" ") # Salidas: 6, 4, 2

3.2.1.17 RESUMEN DE LA SECCIÓN (2/2)

Puntos Clave: continuación


Ejercicio 1
Crea un bucle for que cuente de 0 a 10, e imprima números impares en la pantalla. Usa
el esqueleto de abajo:
for i in range(1, 11):
# Línea de código.
# Línea de código.

Revisar

Solución de muestra:
for i in range(0, 11):
if i % 2 != 0:
print(i)

Ejercicio 2
Crea un bucle while que cuente de 0 a 10, e imprima números impares en la pantalla.
Usa el esqueleto de abajo:
x = 1
while x < 11:
# Línea de código.
# Línea de código.
# Línea de código.

Revisar

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

Ejercicio 3
Crea un programa con un bucle for y una sentencia break . El programa debe iterar
sobre los caracteres en una dirección de correo electrónico, salir del bucle cuando llegue
al símbolo @ e imprimir la parte antes de @ en una línea. Usa el esqueleto de abajo:
for ch in "[email protected]":
if ch == "@":
# Línea de código.
# Línea de código.

Revisar

Solución de muestra:
for ch in "[email protected]":
if ch == "@":
break
print(ch, end="")

Ejercicio 4
Crea un programa con un bucle for y una sentencia continue . El programa debe iterar
sobre una cadena de dígitos, reemplazar cada 0 con x , e imprimir la cadena modificada
en la pantalla. Usa el esqueleto de abajo:
for digit in "0165031806510":
if digit == "0":
# Línea de código.
# Línea de código.
# Línea de código.

Revisar

Solución de muestra:
for digit in "0165031806510":
if digit == "0":
print("x", end="")
continue
print(digit, end="")

Ejercicio 5
¿Cuál es la salida del siguiente código?
n = 3

while n > 0:
print(n + 1)
n -= 1
else:
print(n)

Revisar

4
3
2
0

Ejercicio 5
¿Cuál es la salida del siguiente código?
n = range(4)

for num in n:
print(num - 1)
else:
print(num)

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

3.3.1.1 Operaciones lógicas y de bits en Python | and, or, not

Lógica de computadora
¿Te has dado cuenta de que las condiciones que hemos usado hasta ahora han sido muy
simples, por no decir, bastante primitivas? Las condiciones que utilizamos en la vida real
son mucho más complejas. Veamos este enunciado:

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 tu estás en el centro comercial o yo 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 and. 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:

counter > 0 and value == 100

El resultado proporcionado por el operador and se puede determinar sobre la base de


la tabla de verdad.

Si consideramos la conjunción de A and B , el conjunto de valores posibles de


argumentos y los valores correspondientes de conjunción se ve de la siguiente manera:

Argumento A Argumento B A and B

False False False

False True False

True False False

True True True

or
Un operador de disyunción es la palabra or . Es un operador binario con una prioridad
más baja que and (al igual que + en comparación con * ). Su tabla de verdad es la
siguiente:

Argumento A Argumento B A or B

False False False

False True True

True False True

True True True


not
Además, hay otro operador que se puede aplicar para condiciones de construcción. Es
un operador unario que realiza una negación lógica. Su funcionamiento es simple:
convierte la verdad en falso y lo falso en verdad

Este operador se escribe como la palabra not , y su prioridad es muy alta: igual que el
unario + y - . Su tabla de verdad es simple:

Argumento not Argumento

False True

True False

3.3.1.2 Operaciones lógicas y de bits en Python | and, or, not

Expresiones lógicas
Creemos una variable llamada var y asignémosle 1 . Las siguientes condiciones
son equivalentes a pares:

# Ejemplo 1:
print(var > 0)
print(not (var <= 0))

# Ejemplo 2:
print(var != 0)
print(not (var == 0))

Puedes estar familiarizado con las leyes de De Morgan. Dicen que:

La negación de una conjunción es la separación de las negaciones.

La negación de una disyunción es la conjunción de las negaciones.

Escribamos lo mismo usando Python:

not (p and q) == (not p) or (not q)


not (p or q) == (not p) and (not q)

Observa como se han utilizado los paréntesis para codificar las expresiones: las
colocamos allí para mejorar la legibilidad.
Deberíamos agregar que ninguno de estos operadores de dos argumentos se puede usar
en la forma abreviada conocida como op= . Vale la pena recordar esta excepción.

Valores lógicos frente a bits individuales


Los operadores lógicos toman sus argumentos como un todo, independientemente de
cuantos 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

Operadadores bit a bit


Sin embargo, hay cuatro operadores que le permiten manipular bits de datos
individuales. Se denominan operadores bit a bit.

Cubren todas las operaciones que mencionamos anteriormente en el contexto lógico, y


un operador adicional. Este es el operador xor (significa 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) - o exclusivo a nivel de bits (xor).

Operaciones bit a bit ( & , |, y ^)

Argumento A Argumento B A & B A | B A ^ B

0 0 0 0 0

0 1 0 1 1

1 0 0 1 1

1 1 1 1 0
Operaciones bit a bit ( ~)

Argumento ~ Argumento
0 1

1 0

Hagámoslo más fácil:

• & requieres exactamente dos 1 s para proporcionar 1 como resultado.


• | requiere al menos un 1 para proporcionar 1 como resultado.
• ^ requiere exactamente un 1 para proporcionar 1 como resultado.

Agreguemos un comentario importante: los argumentos de estos operadores deben ser


enteros. No debemos usar flotantes aquí.

La diferencia en el funcionamiento de los operadores lógicos y de bits es importante: los


operadores lógicos no penetran en el nivel de bits de su argumento. Solo les interesa el
valor entero final.

Los operadores bit a bit 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).

3.3.1.3 Operaciones lógicas y de bits en Python

Operaciones lógicas frente a operaciones de bit:


continuación
Ahora te mostraremos un ejemplo de la diferencia en la operación entre las operaciones
lógicas y de bit. Supongamos que se han realizado las siguientes tareas:

i = 15
j = 22

Si asumimos que los enteros se almacenan con 32 bits, la imagen a nivel de bits de las
dos variables será la siguiente:

i: 00000000000000000000000000001111
j: 00000000000000000000000000010110

Se ejecuta la asignación:
log = 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 00000000000000000000000000001111

j 00000000000000000000000000010110

bit = i & j 00000000000000000000000000000110

Estos bits corresponden al valor entero de seis.

Veamos ahora los operadores de negación. Primero el lógico:

logneg = not i

La variable logneg se establecerá en False : no es necesario hacer nada más.

La negación a nivel de bits es así:

bitneg = ~i

Puede ser un poco sorprendente: el valor de la variable bitneg es -16 . Esto puede
parecer extraño, pero no lo es en absoluto. Si deseas obtener más información, debes
consultar el sistema de números binarios y las reglas que rigen los números de
complemento de dos.

i 00000000000000000000000000001111
bitneg = ~i 11111111111111111111111111110000

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

3.3.1.4 Operaciones lógicas y de bits en Python | Operadores bit a


bit

¿Cómo tratamos los bits individuales?


Ahora te mostraremos para que puedes usar los operadores de bit a bit. 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:

flag_register = 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 :

flag_register = 0000000000000000000000000000x000

Es posible que tengas que hacer frente a las siguientes tareas:

1. Comprobar el estado de tu bit: deseas averiguar el valor de su bit; comparar la variable


completa con cero no hará nada, porque los bits restantes pueden tener valores
completamente impredecibles, pero puedes usar la siguiente propiedad de conjunción:

x & 1 = x

x & 0 = 0

Si aplicas la operación & a la variable flagRegister junto con la siguiente imagen de


bits:

00000000000000000000000000001000

(observa el 1 en la posición de tu bit) como resultado, obtendrás una de las siguientes


cadenas de bits:

• 00000000000000000000000000001000 si tu bit se estableció en 1


• 00000000000000000000000000000000 si tu bit se reseteo a 0
Dicha secuencia de ceros y unos, cuya tarea es tomar el valor o cambiar los bits
seleccionados, se denomina máscara de bits.

Construyamos una máscara de bits para detectar el estado de tus bits. Debería apuntar
a el tercer bit. Ese bit tiene el peso de 23=8 . Se podría crear una máscara adecuada
mediante la siguiente sentencia:

the_mask = 8

También puedes hacer una secuencia de instrucciones dependiendo del estado de tu bit,
aquí está:

if flag_register & the_mask:


# Mi bit se estableció en 1.
else:
# Mi bit se restableció a 0.

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 the_mask . Restablecer el bit es simple, y se ve así (elige el que más te
guste):
flag_register = flag_register & ~the_mask
flag_register &= ~the_mask

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:

flag_register = flag_register | the_mask


flag_register |= the_mask

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:

flag_register = flag_register ^ the_mask


flag_register ^= the_mask

3.3.1.5 Operaciones lógicas y de bits en Python | Desplazamiento


de bits

Desplazamiento izquierdo binario y desplazamiento


derecho binario
Python ofrece otra operación relacionada con los bits individuales: shifting. Esto se aplica
solo a los valores de número entero, y no debe usar flotantes como argumentos para ello

Ya aplicas esta operación muy a menudo y muy inconscientemente. ¿Cómo multiplicas


cualquier número por diez? Echa un vistazo:

12345 × 10 = 123450

Como puede ver, multiplicar por diez es de hecho un desplazamiento de todos los dígitos
a la izquierda y llenar el vacío resultante con cero.

¿División entre diez? Echa un vistazo:

12340 ÷ 10 = 1234

Dividir entre diez no es más que desplazar los dígitos a la derecha.

La computadora realiza el mismo tipo de operación, pero con una diferencia: como dos
es la base para los números binarios (no 10), desplazar un valor un bit a la izquierda
corresponde a multiplicarlo por dos; respectivamente, desplazar un bit a la derecha es
como dividir entre dos (observe que se pierde el bit más a la derecha).

Los operadores de cambio en Python son un par de dígrafos: < < y > > , sugiriendo
claramente en qué dirección actuará el cambio.

value << bits


value >> bits

El argumento izquierdo de estos operadores es un valor entero cuyos bits se desplazan.


El argumento correcto determina el tamaño del turno.

Esto demuestra que esta operación ciertamente no es conmutativa


La prioridad de estos operadores es muy alta. Los verás en la tabla de prioridades
actualizada, que te mostraremos al final de esta sección.

Echa un vistazo a los cambios en la ventana del editor.

La invocación final de print() produce el siguiente resultado:

17 68 8
salida

Nota:

• 17 >> 1 → 17 // 2 (17 dividido entre 2 a la potencia de 1) → 8 (desplazarse


hacia la derecha en un bit equivale a la división entera entre dos)
• 17 << 2 → 17 * 4 (17 multiplicado por 2 a la potencia de 2) → 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 ~, +, - unario

2 **

3 *, /, //, %

4 +, - binario

5 <<, >>

6 <, <=, >, >=

7 ==, !=

8 &

9 |

=, +=, -
10
=, *=, /=, %=, &=, ^=, |=, >>=, <<=
var = 17
var_right = var >> 1
var_left = var << 2
print(var, var_left, var_right)

3.3.1.6 RESUMEN DE LA SECCIÓN

Puntos Clave
1. Python es compatible con los siguientes operadores lógicos:

• and → si ambos operandos son verdaderos, la condición es verdadera, por


ejemplo, (True and True) es True .
• or → si alguno de los operandos es verdadero, la condición es verdadera, por
ejemplo, (True or False) es True .
• not → devuelve False si el resultado es verdadero y devuelve True si es falso, por
ejemplo, not True es False .

2. 2. Puedes utilizar operadores bit a bit para manipular bits de datos individuales. Los
siguientes datos de muestra:

• x = 15 , which is 0000 1111 en binario.


• y = 16 , which is 0001 0000 en binario.

Se utilizarán para ilustrar el significado de operadores bit a bit en Python. Analiza los
ejemplos a continuación::

• & hace un bit a bit and (y), por ejemplo, x & y = 0 , el cual es 0000 0000 en
binario.
• | hace un bit a bit or (o), por ejemplo, x | y = 31 , el cual es 0001 1111 en
binario.
• ˜ hace un bit a bit not (no), por ejemplo, ˜ x = 240 , el cual es 1111 0000 en
binario.
• ^ hace un bit a bit xor, por ejemplo, x ^ y = 31 , el cual es 0001 1111 en binario.
• >> hace un desplazamiento bit a bit a la derecha, por ejemplo, y >> 1 = 8 , el
cual es 0000 1000 en binario.
• << hace un desplazamiento bit a bit a la izquierda, por ejemplo, y << 3 = , el
cual es 1000 0000 en binario.

* -16 (decimal del complemento a 2 con signo) -- lee más acerca de la


operación Complemento a dos.

Ejercicio 1

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


x = 1
y = 0

z = ((x == y) and (x == y)) or not(x == y)


print(not(z))

Revisar

False

Ejercicio 2

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

x = 4
y = 1

a = x & y
b = x | y
c = ~x # !difícil!
d = x ^ 5
e = x >> 2
f = x << 2

print(a, b, c, d, e, f)

Revisar

0 5 -5 1 1 16

3.4.1.1 Listas - colecciones de datos

¿Por qué necesitamos listas?


Puede suceder que tengas que leer, almacenar, procesar y, finalmente, imprimir docenas,
quizás cientos, tal vez incluso miles de números. ¿Entonces qué? ¿Necesitas crear una
variable separada para cada valor? ¿Tendrás que pasar largas horas escribiendo
sentencias como la que se muestra a continuación?

var1 = int(input())
var2 = int(input())
var3 = int(input())
var4 = int(input())
var5 = int(input())
var6 = int(input())
:
:
Si no crees que esta sea una tarea complicada, toma un papel y escribe un programa que:

• Lea cinco números.


• Los imprima en orden desde el más pequeño hasta el más grande (Este tipo de
procesamiento se denomina ordenamiento).

Debes percatarte que ni siquiera tienes suficiente papel para completar la tarea.

Hasta ahora, has aprendido como declarar variables que pueden almacenar exactamente
un valor dado a la vez. Tales variables a veces se denominan escalares por analogía con
las matemáticas. Todas las variables que has usado hasta ahora son realmente escalares.

Piensa en lo conveniente que sería declarar una variable que podría almacenar más de
un valor. Por ejemplo, cien, o mil o incluso diez mil. Todavía sería una y la misma variable,
pero muy amplia y espaciosa. ¿Suena atractivo? Quizás, pero ¿cómo manejarías un
contenedor así lleno de valores diferentes? ¿Cómo elegirías solo el que necesitas?

¿Y si solo pudieras numerarlos? Y luego di: dame el valor número 2; asigna el valor
número 15; aumenta el número del valor 10000.

Te mostraremos como declarar tales variables de múltiples valores. Haremos esto con el
ejemplo que acabamos de sugerir. Escribiremos un programa que ordene una secuencia
de números. No seremos particularmente ambiciosos: asumiremos que hay exactamente
cinco números.

Vamos a crear una variable llamada numeros ; se le asigna no solo un número, sino que
se llena con una lista que consta de cinco valores (nota: la lista comienza con un corchete
abierto y termina con un corchete cerrado; el espacio entre los corchetes es llenado con
cinco números separados por comas).

numbers = [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 sentencia
crea una lista de longitud igual a cinco (ya que contiene cinco elementos).

Los elementos dentro de una lista pueden tener diferentes tipos. Algunos de ellos
pueden ser enteros, otros son flotantes y otros pueden ser listas.

Python ha adoptado una convención que indica que los elementos de una lista
están siempre numerados desde cero. Esto significa que el elemento almacenado al
principio de la lista tendrá el número cero. Como hay cinco elementos en nuestra lista, al
último de ellos se le asigna el número cuatro. No olvides esto.

Pronto te acostumbrarás y se convertirá en algo natural.

Antes de continuar con nuestra discusión, debemos indicar lo siguiente: nuestra lista es
una colección de elementos, pero cada elemento es un escalar
3.4.1.2 Listas - colecciones de datos | Indexación

Indexando Listas
¿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:

numbers = [10, 5, 7, 2, 1]

print("Contenido de la lista original:", numbers) # Imprimiendo


contenido de la lista original.

numbers[0] = 111

print("Nuevo contenido de la lista: ", numbers) # Contenido de la


lista actual.

Y ahora queremos copiar el valor del quinto elemento al segundo elemento. ¿Puedes
adivinar cómo hacerlo?

numbers = [10, 5, 7, 2, 1]

print("Contenido de la lista original:", numbers) # Imprimiendo


contenido de la lista original.

numbers[0] = 111

print("\nPrevio contenido de la lista:", numbers) # Imprimiendo


contenido de la lista anterior.

numbers[1] = numbers[4] # Copiando el valor del quinto elemento al


segundo elemento.

print("Nuevo contenido de la lista:", numbers) # 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.
numbers = [10, 5, 7, 2, 1]
print("Contenido de la lista:", numbers) # Imprimiendo contenido
# de la lista original.

3.4.1.3 Listas - colecciones de datos | Indexación

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(numbers[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(numbers) # 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]
output

La función len()
La longitud de una lista puede variar durante la ejecución. Se pueden agregar nuevos
elementos a la lista, mientras que otros pueden eliminarse de ella. Esto significa que la
lista es una entidad muy dinámica.

Si deseas verificar la longitud actual de la lista, puedes usar una función


llamada len() (su nombre proviene de length - longitud).

La función toma el nombre de la lista como un argumento y devuelve el número de


elementos almacenados actualmente dentro de la lista (en otras palabras, la longitud de
la lista).

Observa la última línea de código en el editor, ejecuta el programa y verifica que valor
imprimirá en la consola. ¿Puedes adivinar?

numbers = [10, 5, 7, 2, 1]
print("Contenido de la lista original:", numbers) # Imprimiendo el contenido
# de la lista original.
numbers[0] = 111

# Imprimiendo contenido de la lista con 111.


print("\nContenido de la lista con cambio:", numbers)

# Copiando el valor del quinto elemento al segundo elemento.


numbers[1] = numbers[4]

# Imprimiendo contenido de la lista con intercambio.


print("Contenido de la lista con intercambio:", numbers)

# Imprimiendo la longitud de la lista.


print("\nLongitud de la lista:", len(numbers))

3.4.1.4 Listas - colecciones de datos | Operaciones en listas

Eliminando elementos de una lista


Cualquier elemento de la lista puede ser eliminado en cualquier momento, esto se hace
con una instrucción llamada del (eliminar). Nota: es una instrucción, no una función.

Tienes que apuntar al elemento que quieres eliminar, desaparecerá de la lista y la


longitud de la lista se reducirá en uno.

Mira el fragmento de abajo. ¿Puedes adivinar qué salida producirá? Ejecuta el programa
en el editor y comprueba.

del numbers[1]
print(len(numbers))
print(numbers)

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(numbers[4])
numbers[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.

numbers = [10, 5, 7, 2, 1]

# Imprimiendo el contenido de la lista original.


print("Contenido de la lista original:", numbers)

numbers[0] = 111

# Imprimiendo contenido de la lista con 111.


print("\nContenido de la lista con cambio:", numbers)

# Copiando el valor del quinto elemento al segundo elemento.


numbers[1] = numbers[4]

# Imprimiendo contenido de la lista con intercambio.


print("Contenido de la lista con intercambio:", numbers)

# Imprimiendo la longitud de la lista.


print("\nLongitud de la lista:", len(numbers))

# Eliminando el segundo elemento de la lista.


del numbers[1]

# Imprimiendo nueva longitud de la lista.


print("Longitud de la nueva lista:", len(numbers))

# Imprimiendo el contenido de la lista actual.


print("\nNuevo contenido de la lista:", numbers)

3.4.1.5 Listas - colecciones de datos | Operaciones en listas

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(numbers[-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(numbers[-2])

El fragmento de ejemplo dará como salida un 2 .

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

numbers = [111, 7, 2, 1]
print(numbers[-1])
print(numbers[-2])

3.4.1.6 LABORATORIO: Lo básico de las listas


LABORATORIO

Tiempo Estimado
5 minutos

Nivel de Dificultad
Muy fácil

Objetivos
Familiarizar al estudiante con:

• Usar instrucciones básicas relacionadas con listas.


• Crear y modificar listas.

Escenario
Había una vez un sombrero. El sombrero no contenía conejo, sino una lista de cinco
números: 1 , 2 , 3 , 4 y 5 .

Tu tarea es:

• Escribir una línea de código que solicite al usuario que reemplace el número
central en la lista con un número entero ingresado por el usuario (Paso 1).
• Escribir una línea de código que elimine el último elemento de la lista (Paso 2).
• Escribir una línea de código que imprima la longitud de la lista existente (Paso 3).

¿Listo para este desafío?

# Esta es una lista existente de números ocultos en el sombrero.


hat_list = [1, 2, 3, 4, 5]

# Paso 1: escribe una línea de código que solicite al usuario


# 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(hat_list)
3.4.1.7 Listas - colecciones de datos | Funciones y métodos

Funciones frente a 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:

result = function(arg)

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

result = data.method(arg)

Nota: el nombre del método está precedido por el nombre de los datos que posee el
método. A continuación, se agrega un punto, seguido del nombre del método y un par
de paréntesis que encierran los argumentos.

El método se comportará como una función, pero puede hacer algo más: puede cambiar
el estado interno de los datos a partir de los cuales se ha invocado.

Puedes preguntar: ¿por qué estamos hablando de métodos, y no de listas?

Este es un tema esencial en este momento, ya que le mostraremos como agregar nuevos
elementos a una lista existente. Esto se puede hacer con métodos propios de las listas,
no por funciones.
3.4.1.8 Listas - colecciones de datos | Métodos de list

Agregando elementos a una lista: append() e insert()


Un nuevo elemento puede ser añadido al final de la lista existente:

list.append(value)

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.

list.insert(location, value)

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:

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

numbers = [111, 7, 2, 1]
print(len(numbers))
print(numbers)

###

numbers.append(4)

print(len(numbers))
print(numbers)

###

numbers.insert(0, 222)
print(len(numbers))
print(numbers)

3.4.1.9 Listas - colecciones de datos | Métodos de listas

Agregando elementos a una lista: continuación


Puedes iniciar la vida de una lista creándola vacía (esto se hace con un par de corchetes
vacíos) y luego agregar nuevos elementos según sea necesario.

Echa un vistazo al fragmento en el editor. Intenta adivinar su salida después de la


ejecución del bucle for . Ejecuta el programa para comprobar si tenías razón.

Será una secuencia de números enteros consecutivos del 1 (luego agrega uno a todos los
valores agregados) hasta 5 .

Hemos modificado un poco el fragmento:

my_list = [] # Creando una lista vacía.

for i in range(5):
my_list.insert(0, i + 1)

print(my_list)

¿Qué pasará ahora? Ejecuta el programa y comprueba si esta vez también tenías razón.

Deberías obtener la misma secuencia, pero en orden inverso (este es el mérito de usar el
método insert() ).

my_list = [] # Creando una lista vacía.

for i in range(5):
my_list.append(i + 1)

print(my_list)

3.4.1.10 Listas - colecciones de datos | Listas y bucles


Haciendo uso de las listas
El bucle for tiene una variante muy especial que puede procesar las listas de manera
muy efectiva. Echemos un vistazo a eso.

Supongamos que deseas calcular la suma de todos los valores almacenados en la


lista my_list .

Necesitas una variable cuya suma se almacenará y se le asignará inicialmente un valor


de 0 - su nombre será total . (Nota: no la vamos a nombrar sum ya que Python utiliza el
mismo nombre para una de sus funciones integradas: sum() . Utilizar ese nombre sería
considerado una mala práctica. Luego agrega todos los elementos de la lista usando el
bucle for . Echa un vistazo al fragmento en el editor.

Comentemos este ejemplo:

• A la lista se le asigna una secuencia de cinco valores enteros.


• La variable i toma los valores 0 , 1 , 2 , 3 , y 4 , y luego indexa la lista, seleccionando
los elementos siguientes: el primero, segundo, tercero, cuarto y quinto.
• Cada uno de estos elementos se agrega junto con el operador += a la
variable suma , dando el resultado final al final del bucle.
• Observa la forma en que se ha empleado la función len() , hace que el código sea
independiente de cualquier posible cambio en el contenido de la lista.

El segundo aspecto del bucle for


Pero el bucle for puede hacer mucho más. Puede ocultar todas las acciones conectadas
a la indexación de la lista y entregar todos los elementos de la lista de manera práctica.

Este fragmento modificado muestra como funciona:

my_list = [10, 1, 8, 3, 5]
total = 0

for i in my_list:
total += i

print(total)

¿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 ( my_list ).
• A la variable i se le asignan los valores de todos los elementos de la lista
subsiguiente, y el proceso ocurre tantas veces como hay elementos en la lista.
• Esto significa que usa la variable i como una copia de los valores de los
elementos, y no necesita emplear índices.
• La función len() tampoco es necesaria aquí.
my_list = [10, 1, 8, 3, 5]
total = 0

for i in range(len(my_list)):
total += my_list[i]

print(total)

3.4.1.11 Listas - colecciones de datos | Listas y bucles


Echa un vistazo al fragmento:

variable_1 = 1
variable_2 = 2

variable_2 = variable_1
variable_1 = variable_2

Si haces algo como esto, perderás el valor previamente almacenado en variable_2 .


Cambiar el orden de las tareas no ayudará. Necesitas una tercera variable que sirva como
almacenamiento auxiliar.

Así es como puedes hacerlo:

variable_1 = 1
variable_2 = 2

auxiliary = variable_1
variable_1 = variable_2
variable_2 = auxiliary

Python ofrece una forma más conveniente de hacer el intercambio, echa un vistazo:

variable_1 = 1
variable_2 = 2

variable_1, variable_2 = variable_2, variable_1

Claro, efectivo y elegante, ¿no?

3.4.1.12 Listas - colecciones de datos | Listas y bucles

Listas en acción
Ahora puedes intercambiar fácilmente los elementos de la lista para revertir su orden:
my_list = [10, 1, 8, 3, 5]

my_list[0], my_list[4] = my_list[4], my_list[0]


my_list[1], my_list[3] = my_list[3], my_list[1]

print(my_list)

Ejecuta el fragmento. Su salida debería verse así:

[5, 3, 8, 1, 10]
salida

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:

my_list = [10, 1, 8, 3, 5]
length = len(my_list)

for i in range(length // 2):


my_list[i], my_list[length - i - 1] = my_list[length - i - 1],
my_list[i]

print(my_list)

Nota:
• Hemos asignado la variable length a la longitud de la lista actual (esto hace que
nuestro código sea un poco más claro y más corto).
• Hemos preparado el bucle for para que se ejecute su cuerpo length // 2 veces
(esto funciona bien para listas con longitudes pares e impares, porque cuando la
lista contiene un número impar de elementos, el del medio permanece intacto).
• Hemos intercambiado el elemento i (desde el principio de la lista) por el que tiene
un índice igual a (length-i-1) (desde el final de la lista); en nuestro ejemplo,
for i igual a 0 a la (length-i-1) da 4 ; for i igual a 3 , da 3 : esto es exactamente
lo que necesitábamos.

Las listas son extremadamente útiles y las encontrarás muy a menudo.

3.4.1.13 LABORATORIO: Lo básico de las listas - Los Beatles

LABORATORIO
Tiempo Estimado
10-15 minutos

Nivel de Dificultad
Fácil

Objetivos
Familiarizar al estudiante con:

• Crear y modificar listas simples.


• Utilizar métodos para modificar listas.

Escenario
Los Beatles fueron uno de los grupos de música más populares de la década de 1960 y la
banda más vendida en la historia. Algunas personas los consideran el acto más influyente
de la era del rock. De hecho, se incluyeron en la compilación de la revista Time de las 100
personas más influyentes del siglo XX.

a 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 bucle for y el append() para pedirle al usuario que agregue los
siguientes miembros de la banda a la lista: Stu Sutcliffe , y Pete Best .
• Paso 4: Usa la instrucción del para eliminar a Stu Sutcliffe y Pete Best de la
lista.
• Paso 5: Usa el método insert() para agregar a Ringo Starr al principio de la
lista.

Por cierto, ¿eres fan de los Beatles? (Los Beatles son una de las bandas favoritas de Greg.
Pero espera...¿Quién es Greg?)

# paso 1
print("Paso 1:", Beatles)

# paso 2
print("Paso 2:", Beatles)
# paso 3
print("Paso 3:", Beatles)

# paso 4
print("Paso 4:", Beatles)

# paso 5
print("Paso 5:", Beatles)

# probando la longitud de la lista


print("Los Fav", len(Beatles))

3.4.1.14 RESUMEN DE LA SECCIÓN


Puntos Clave

1. La lista es un tipo de dato en Python que se utiliza para almacenar múltiples objetos. Es
una colección ordenada y mutable de elementos separados por comas entre corchetes,
por ejemplo:

my_list = [1, None, True, "Soy una cadena", 256, 0]

2. Las listas se pueden indexar y actualizar , por ejemplo:

my_list = [1, None, True, 'Soy una cadena', 256, 0]


print(my_list[3]) # salida: Soy una cadena
print(my_list[-1]) # salida: 0

my_list[1] = '?'
print(my_list) # salida: [1, '?', True, 'Soy una cadena', 256, 0]

my_list.insert(0, "primero")
my_list.append("último")
print(my_list) # outputs: ['primero', 1, '?', True, 'Soy una
cadena', 256, 0, 'último']

3. Las listas pueden estar anidadas, por ejemplo:

my_list = [1, 'a', ["lista", 64, [0, 1], False]]

Aprenderás más sobre el anidamiento en el módulo 3.7; por el momento, solo queremos
que sepas que algo como esto también es posible.

4. Los elementos de la lista y las listas se pueden eliminar, por ejemplo:

my_list = [1, 2, 3, 4]
del my_list[2]
print(my_list) # salida: [1, 2, 4]
del my_list # borra la lista entera

Nuevamente, aprenderás más sobre esto en el módulo 3.6, no te preocupes. Por el


momento, intenta experimentar con el código anterior y verifica cómo cambiarlo afecta la
salida.

5.Las listas pueden ser iteradas mediante el uso del bucle for , por ejemplo:
my_list = ["blanco", "purpura", "azul", "amarillo", "verde"]

for color in my_list:


print(color)

6. La función len() se puede usar para verificar la longitud de la lista, por ejemplo:

my_list = ["blanco", "purpura", "azul", "amarillo", "verde"]


print(len(my_list)) # salida 5

del my_list[2]
print(len(my_list)) # salida 4

. Una invocación típica de función tiene el siguiente aspecto: result = function(arg) ,


mientras que una invocación típica de un método se ve así: result =
data.method(arg) .

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

lst = [1, 2, 3, 4, 5]
lst.insert(1, 6)
del lst[0]
lst.append(1)

print(lst)

Revisar

[6, 2, 3, 4, 5, 1]

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

lst = [1, 2, 3, 4, 5]
lst_2 = []
add = 0

for number in lst:


add += number
lst_2.append(add)

print(lst_2)
Revisar

[1, 3, 6, 10, 15]

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

lst = []
del lst
print(lst)

Revisar

NameError: name 'lst' is not defined

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

lst = [1, [2, 3], 4]


print(lst[1])
print(len(lst))

Revisar
[2, 3]
3

3.5.1.1 Ordenando listas simples - el ordenamiento de burbuja

Ordenamiento Burbuja
Ahora que puedes hacer malabarismos con los elementos de las listas, es hora de
aprender como ordenarlos. Se han inventado muchos algoritmos de clasificación, que
difieren mucho en velocidad, así como en complejidad. Vamos a mostrar un algoritmo
muy simple, fácil de entender, pero desafortunadamente, tampoco es muy eficiente. Se
usa muy raramente, y ciertamente no para listas extensas.

Digamos que una lista se puede ordenar de dos maneras:

• Ascendente (o más precisamente, no descendente): si en cada par de elementos


adyacentes, el primer elemento no es mayor que el segundo.
• Descendente (o más precisamente, no ascendente): si en cada par de elementos
adyacentes, el primer elemento no es menor que el segundo.

En las siguientes secciones, ordenaremos la lista en orden ascendente, de modo que los
números se ordenen de menor a mayor.

Aquí está la lista:


8 10 6 2 4

Intentaremos utilizar el siguiente enfoque: tomaremos el primer y el segundo elemento y


los compararemos; si determinamos que están en el orden incorrecto (es decir, el
primero es mayor que el segundo), los intercambiaremos; Si su orden es válido, no
haremos nada. Un vistazo a nuestra lista confirma lo último: los elementos 01 y 02 están
en el orden correcto, así como 8<10 .

Ahora observa el segundo y el tercer elemento. Están en las posiciones equivocadas.


Tenemos que intercambiarlos:

8 6 10 2 4

Vamos más allá y observemos los elementos tercero y cuarto. Una vez más, esto no es lo
que se supone que es. Tenemos que intercambiarlos:

8 6 2 10 4

Ahora comprobemos los elementos cuarto y quinto. Si, ellos también están en las
posiciones equivocadas. Ocurre otro intercambio:

8 6 2 4 10

El primer paso a través de la lista ya está terminado. Todavía estamos lejos de terminar
nuestro trabajo, pero algo curioso ha sucedido mientras tanto. El elemento más
grande, 10 , ya ha llegado al final de la lista. Ten en cuenta que este es el lugar
deseado para el. Todos los elementos restantes forman un lío pintoresco, pero este ya
está en su lugar.

Ahora, por un momento, intenta imaginar la lista de una manera ligeramente diferente,
es decir, de esta manera:

10
4
2
6
8

Observa - El 10 está en la parte superior. Podríamos decir que flotó desde el fondo hasta
la superficie, al igual que las burbujas en una copa de champán. El método de
clasificación deriva su nombre de la misma observación: se denomina ordenamiento de
burbuja.
Ahora comenzamos con el segundo paso a través de la lista. Miramos el primer y el
segundo elemento, es necesario un intercambio:

6 8 2 4 10

Tiempo para el segundo y tercer elemento: también tenemos que intercambiarlos:


6 2 8 4 10

Ahora el tercer y cuarto elementos, y la segunda pasada, se completa, ya que 8 ya está en


su lugar:
6 2 4 8 10

Comenzamos el siguiente pase inmediatamente. Observe atentamente el primer y el


segundo elemento: se necesita otro cambio:
2 6 4 8 10

Ahora 6 necesita ir a su lugar. Cambiamos el segundo y el tercer elemento:


2 4 6 8 10

La lista ya está ordenada. No tenemos nada más que hacer. Esto es exactamente lo que
queremos.

Como puedes ver, la esencia de este algoritmo es simple: comparamos los elementos
adyacentes y, al intercambiar algunos de ellos, logramos nuestro objetivo.

Codifiquemos en Python todas las acciones realizadas durante un solo paso a través de la
lista, y luego consideraremos cuántos pases necesitamos para realizarlo. No hemos
explicado esto hasta ahora, pero lo haremos pronto.

3.5.1.2 Ordenando listas simples - el 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 .

my_list = [8, 10, 6, 2, 4] # lista a ordenar

for i in range(len(my_list) - 1): # necesitamos (5 - 1) comparaciones


if my_list[i] > my_list[i + 1]: # compara elementos adyacentes
# Si terminamos aquí, tenemos que intercambiar elementos.
my_list[i], my_list[i + 1] = my_list[i + 1], my_list[i]

Deberías poder leer y comprender este programa sin ningún problema:

my_list = [8, 10, 6, 2, 4] # lista a ordenar


swapped = True # Lo necesitamos verdadero (True) para ingresar al bucle while.

while swapped:
swapped = False # no hay intercambios hasta ahora
for i in range(len(my_list) - 1):
if my_list[i] > my_list[i + 1]:
swapped = True # ¡ocurrió el intercambio!
my_list[i], my_list[i + 1] = my_list[i + 1], my_list[i]

print(my_list)

Ejecuta el programa y pruébalo.

3.5.1.3 Ordenando listas simples - el ordenamiento de burbuja

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 final interactiva.

Python, sin embargo, tiene sus propios mecanismos de clasificación. Nadie necesita
escribir sus propias clases, ya que hay un número suficiente de herramientas listas para
usar.

Te explicamos este sistema de clasificación porque es importante aprender como


procesar los contenidos de una lista y mostrarte como puede funcionar la clasificación
real.

Si quieres que Python ordene tu lista, puedes hacerlo de la siguiente manera:

my_list = [8, 10, 6, 2, 4]


my_list.sort()
print(my_list)

Es tan simple como eso.


La salida del fragmento es la siguiente:

[2, 4, 6, 8, 10]
salida
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.

my_list = []
swapped = True
num = int(input("¿Cuántos elementos deseas ordenar?: "))

for i in range(num):
val = float(input("Ingresa un elemento de la lista: "))
my_list.append(val)

while swapped:
swapped = False
for i in range(len(my_list) - 1):
if my_list[i] > my_list[i + 1]:
swapped = True
my_list[i], my_list[i + 1] = my_list[i + 1],
my_list[i]

print("\nOrdenada:")
print(my_list)

3.5.1.4 RESUMEN DE LA SECCIÓN

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

lst = [5, 3, 1, 2, 4]
print(lst)

lst.sort()
print(lst) # outputs: [1, 2, 3, 4, 5]

2.También hay un método de lista llamado reverse() , que puedes usar para invertir la
lista, por ejemplo:

lst = [5, 3, 1, 2, 4]
print(lst)

lst.reverse()
print(lst) # salida: [4, 2, 1, 3, 5]

Ejercicio 1
¿Cuál es la salida del siguiente fragmento de código?
lst = ["D", "F", "A", "Z"]
lst.sort()

print(lst)

Revisar

['A', 'D', 'F', 'Z']

Ejercicio 2
¿Cuál es la salida del siguiente fragmento de código?
a = 3
b = 1
c = 2

lst = [a, c, b]
lst.sort()

print(lst)

Revisar

[1, 2, 3]

Ejercicio 3
¿Cuál es la salida del siguiente fragmento de código?
a = "A"
b = "B"
c = "C"
d = " "

lst = [a, b, c, d]
lst.reverse()

print(lst)

Revisar

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

3.6.1.1 Operaciones en listas

La vida al interior de las listas


Ahora queremos mostrarte una característica importante y muy sorprendente de las
listas, que las distingue de las variables ordinarias.

Queremos que lo memorices, ya que puede afectar tus programas futuros y causar
graves problemas si se olvida o se pasa por alto.
Echa un vistazo al fragmento en el editor.

El programa:

• Crea una lista de un elemento llamada list_1 .


• La asigna a una nueva lista llamada list_2 .
• Cambia el único elemento de list_1 .
• Imprime la list_2 .

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: list_2 = list_1 copia el nombre del arreglo no su contenido. En efecto,


los dos nombres ( list_1 y list_2 ) 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?

list_1 = [1]
list_2 = list_1
list_1[0] = 2
print(list_2)

3.6.1.2 Operaciones en listas - rebanadas

Rebanadas Poderosas
Afortunadamente, la solución está al alcance de tu mano: su nombre es rebanada.

Una rebanada 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:
list_1 = [1]
list_2 = list_1[:]
list_1[0] = 2
print(list_2)

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 rebanada es la siguiente:

my_list[start:end]

Como puedes ver, se asemeja a la indexación, pero los dos puntos en el interior hacen
una gran diferencia.

Una rebanada 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:

my_list = [10, 8, 6, 4, 2]
new_list = my_list[1:3]
print(new_list)

La lista new_list contendrá fin - inicio (3 - 1 = 2) elementos, los que tienen índices
iguales a 1 y 2 (pero no 3 ).

La salida del fragmento es: [8, 6]

# Copiando la lista entera.


list_1 = [1]
list_2 = list_1[:]
list_1[0] = 2
print(list_2)

# Copiando parte de la lista.


my_list = [10, 8, 6, 4, 2]
new_list = my_list[1:3]
print(new_list)
3.6.1.3 Operaciones en listas - rebanadas

Rebanadas - índices negativos


Observa el fragmento de código a continuación:

my_list[start:end]

Para confirmar:

• start es el índice del primer elemento incluido en la rebanada.


• end es el índice del primer elemento no incluido en la rebanada.

Así es como los índices negativos funcionan en las rebanadas:

my_list = [10, 8, 6, 4, 2]
new_list = my_list[1:-1]
print(new_list)

El resultado del fragmento es:

[8, 6, 4]
salida

Si start especifica un elemento que se encuentra más allá del descrito por end (desde el
punto de vista inicial de la lista), la rebanada estará vacía:

my_list = [10, 8, 6, 4, 2]
new_list = my_list[-1:1]
print(new_list)

La salida del fragmento es:

[]
salida

3.6.1.4 Operaciones en listas - rebanadas

Rebanadas: continuación
Si omites el start en tu rebanada, se supone que deseas obtener un segmento que
comienza en el elemento con índice 0 .

En otras palabras, la rebanada sería de esta forma:

my_list[:end]
es un equivalente más compacto de:

my_list[0:end]

Observa el fragmento de código a continuación:

my_list = [10, 8, 6, 4, 2]
new_list = my_list[:3]
print(new_list)

Es por esto que su salida es: [10, 8, 6] .

Del mismo modo, si omites el end en tu rebanada, se supone que deseas que el
segmento termine en el elemento con el índice len(my_list) .

En otras palabras, la rebanada sería de esta forma:

my_list[start:]

es un equivalente más compacto de:

my_list[start:len(my_list)]

Observa el siguiente fragmento de código:

my_list = [10, 8, 6, 4, 2]
new_list = my_list[3:]
print(new_list)

Por lo tanto, la salida es: [4, 2] .

3.6.1.5 Operaciones en listas ‒ rebanadas, del(eliminar)

Rebanadas: continuación
Como hemos dicho anteriormente, el omitir el inicio y fin crea una copia de toda la
lista:

my_list = [10, 8, 6, 4, 2]
new_list = my_list[:]
print(new_list)
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 rebanadas:

my_list = [10, 8, 6, 4, 2]
del my_list[1:3]
print(my_list)

Nota: En este caso, la rebanada ¡no produce ninguna lista nueva!

La salida del fragmento es: [10, 4, 2] .

También es posible eliminar todos los elementos a la vez:

my_list = [10, 8, 6, 4, 2]
del my_list[:]
print(my_list)

La lista se queda vacía y la salida es: [] .

Al eliminar la rebanada del código, su significado cambia dramáticamente.

Echa un vistazo:

my_list = [10, 8, 6, 4, 2]
del my_list
print(my_list)

La instrucción del eliminará la lista, no su contenido.

La función print() de la última línea del código provocará un error de ejecución.

3.6.1.6 Operaciones en listas ‒ in, not in

Los operadores in y not in


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 my_list
elem not in my_list
El primero de ellos ( in ) verifica si un elemento dado (el 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 (el 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.

my_list = [0, 3, 12, 8, 2]

print(5 in my_list)
print(5 not in my_list)
print(12 in my_list)

3.6.1.7 Listas - más detalles

Listas - algunos programas simples


Ahora queremos mostrarte algunos programas simples que utilizan listas.

El primero de ellos intenta encontrar el valor mayor 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
bucle for :

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


largest = my_list[0]

for i in my_list:
if i > largest:
largest = i

print(largest)
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 rebanada:

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


largest = my_list[0]

for i in my_list[1:]:
if i > largest:
largest = i

print(largest)

La pregunta es: ¿Cuál de estas dos acciones consume más recursos informáticos: solo
una comparación o rebanar casi todos los elementos de una lista?

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


largest = my_list[0]

for i in range(1, len(my_list)):


if my_list[i] > largest:
largest = my_list[i]

print(largest)

3.6.1.8 Listas - más detalles

Listas - algunos programas simples


Ahora encontremos la ubicación de un elemento dado dentro de una lista:

my_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]


to_find = 5
found = False

for i in range(len(my_list)):
found = my_list[i] == to_find
if found:
break

if found:
print("Elemento encontrado en el índice", i)
else:
print("ausente")
Nota:

• El valor buscado se almacena en la variable to_find .


• El estado actual de la búsqueda se almacena en la variable found ( True / False ).
• Cuando found 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?

Este programa te dará la respuesta:

drawn = [5, 11, 9, 42, 3, 49]


bets = [3, 7, 11, 42, 34, 49]
hits = 0

for number in bets:


if number in drawn:
hits += 1

print(hits)

Nota:

• La lista drawn almacena todos los números sorteados.


• La lista bets almacena los números con que se juega.
• La variable hits cuenta tus aciertos.

La salida del programa es: 4 .

3.6.1.9 LABORATORIO: Operando con listas - conceptos básicos

LABORATORIO

Tiempo Estimado
10-15 minutos

Nivel de Dificultad
Fácil
Objetivos
Familiarizar al estudiante con:

• Indexación de listas.
• Utilizar operadores in y not in .

Escenario
Imagina una lista: no muy larga ni muy complicada, solo una lista simple que contiene
algunos números enteros. Algunos de estos números pueden estar repetidos, y esta es la
clave. No queremos ninguna repetición. Queremos que sean eliminados.

Tu tarea es escribir un programa que elimine todas las repeticiones de números de la


lista. El objetivo es tener una lista en la que todos los números aparezcan no más de una
vez.

Nota: Asume que la lista original está ya dentro del código, no tienes que ingresarla desde
el teclado. Por supuesto, puedes mejorar el código y agregar una parte que pueda llevar a
cabo una conversación con el usuario y obtener todos los datos.

Sugerencia: Te recomendamos que crees una nueva lista como área de trabajo temporal,
no necesitas actualizar la lista actual.

No hemos proporcionado datos de prueba, ya que sería demasiado fácil. Puedes usar
nuestro esqueleto en su lugar.

my_list = [1, 2, 4, 4, 1, 4, 2, 6, 2, 9]
#
# Escribe tu código aquí.
#
print("La lista con elementos únicos:")
print(my_list)

3.6.1.10 RESUMEN DE LA SECCIÓN

Puntos Clave
1. Si tienes una lista list_1 , y la siguiente asignación: list_2 = list_1 esto no hace
una copia de la lista list_1 , pero hace que las variables list_1 y list_2 apunten a la
misma lista en la memoria. Por ejemplo:

vehicles_one = ['carro', 'bicicleta', 'motor']


print(vehicles_one) # salida: [carro', 'bicicleta', 'motor']

vehicles_two = vehicles_one
del vehicles_one[0] # elimina 'carro'
print(vehicles_two) # salida: ['bicicleta', 'motor']

2. Si deseas copiar una lista o parte de la lista, puedes hacerlo haciendo uso
de rebanadas:

colors = ['rojo', 'verde', 'naranja']

copy_whole_colors = colors[:] # copia la lista entera


copy_part_colors = colors[0:2] # copia parte de la lista

3. También puede utilizar índices negativos para hacer uso de rebanadas. Por ejemplo:

sample_list = ["A", "B", "C", "D", "E"]


new_list = sample_list[2:-1]
print(new_list) # outputs: ['C', 'D']

4. Los parámetros start y end son opcionales al partir en rebanadas una


lista: list[start:end] , por ejemplo:

my_list = [1, 2, 3, 4, 5]
slice_one = my_list[2: ]
slice_two = my_list[ :2]
slice_three = my_list[-2: ]

print(slice_one) # salida: [3, 4, 5]


print(slice_two) # salida: [1, 2]
print(slice_three) # salida: [4, 5]

5. Puedes eliminar rebanadas utilizando la instrucción del :

my_list = [1, 2, 3, 4, 5]
del my_list[0:2]
print(my_list) # salida: [3, 4, 5]

del my_list[:]
print(my_list) # delimina 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:

my_list = ["A", "B", 1, 2]

print("A" in my_list) # salida: True


print("C" not in my_list) # salida: True
print(2 not in my_list) # salida: False
Ejercicio 1

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

list_1 = ["A", "B", "C"]


list_2 = list_1
list_3 = list_2

del list_1[0]
del list_2[0]

print(list_3)

Revisar

['C']

Ejercicio 2

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

list_1 = ["A", "B", "C"]


list_2 = list_1
list_3 = list_2

del list_1[0]
del list_2

print(list_3)

Revisar

['B', 'C']

Ejercicio 3

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

list_1 = ["A", "B", "C"]


list_2 = list_1
list_3 = list_2

del list_1[0]
del list_2[:]

print(list_3)

Revisar
[]

Ejercicio 4

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

list_1 = ["A", "B", "C"]


list_2 = list_1[:]
list_3 = list_2[:]

del list_1[0]
del list_2[0]

print(list_3)

Revisar

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

Ejercicio 5

Inserta in o not in en lugar de ??? para que el código genere el resultado esperado.

my_list = [1, 2, "in", True, "ABC"]

print(1 ??? my_list) # salida True


print("A" ??? my_list) # salida True
print(3 ??? my_list) # salida True
print(False ??? my_list) # salida False

Revisar
my_list = [1, 2, "in", True, "ABC"]
print(1 in my_list) # salida True
print("A" not in my_list) # salida True
print(3 not in my_list) # salida True
print(False in my_list) # salida False

3.7.1.1 Listas en aplicaciones avanzadas

Listas dentro de listas


Las listas pueden constar de escalares (es decir, números) y elementos de una estructura
mucho más compleja (ya has visto ejemplos como cadenas, booleanos o incluso otras
listas en las lecciones del Resumen de la Sección anterior). Veamos más de cerca el caso
en el que los elementos de una lista son listas.

A menudo encontramos estos arreglos en nuestras vidas. Probablemente el mejor


ejemplo de esto sea un tablero de ajedrez.
Un tablero de ajedrez está compuesto de filas y columnas. Hay ocho filas y ocho
columnas. Cada columna está marcada con las letras de la A a la H. Cada línea está
marcada con un número del uno al ocho.

La ubicación de cada campo se identifica por pares de letras y dígitos. Por lo tanto,
sabemos que la esquina inferior derecha del tablero (la que tiene la torre blanca) es A1,
mientras que la esquina opuesta es H8.

Supongamos que podemos usar los números seleccionados para representar cualquier
pieza de ajedrez. También podemos asumir que cada fila en el tablero de ajedrez es una
lista.

Observa el siguiente código:

row = []

for i in range(8):
row.append(WHITE_PAWN)

Crea una lista que contiene ocho elementos que representan la segunda fila del tablero
de ajedrez: la que está llena de peones (supon que WHITE_PAWN 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:

row = [WHITE_PAWN 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 ( WHITE_PAWN )


• La cláusula que especifica cuántas veces se producen los datos dentro de la lista
( for i in range(8) )

Permítenos mostrarte otros ejemplos de comprensión de lista:

Ejemplo #1:

squares = [x ** 2 for x in range(10)]


El fragmento de código genera una lista de diez elementos y la rellena con cuadrados de
diez números enteros que comienzan desde cero (0, 1, 4, 9, 16, 25, 36, 49, 64, 81)

Ejemplo #2:

twos = [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:

odds = [x for x in squares if x % 2 != 0 ]

El fragmento crea una lista con solo los elementos impares de la lista squares .

3.7.1.2 Listas en aplicaciones avanzadas - Arreglos

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:

board = []

for i in range(8):
row = [EMPTY for i in range(8)]
board.append(row)

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 board .
• La parte exterior se repite ocho veces.
• En total, la lista board 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 board 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:

board = [[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.

3.7.1.3 Listas en aplicaciones avanzadas - Arreglos

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:
EMPTY = "-"
PAWN = "PEON"
ROOK = "TORRE"
KNIGHT = "CABALLO"
board = []

for i in range(8):
row = [EMPTY for i in range(8)]
board.append(row)

board[0][0] = ROOK
board[0][7] = ROOK
board[7][0] = ROOK
board[7][7] = ROOK

print(board)

3.7.1.4 Listas en aplicaciones avanzadas - Arreglos

Naturaleza multidimensional de las listas: aplicaciones


avanzadas
Profundicemos en la naturaleza multidimensional de las listas. Para encontrar cualquier
elemento de una lista bidimensional, debes usar dos coordenadas:

• Una vertical (número de fila).


• Una horizontal (número de columna).

Imagina que desarrollas una pieza de software para una estación meteorológica
automática. El dispositivo registra la temperatura del aire cada hora y lo hace durante
todo el mes. Esto te da un total de 24 × 31 = 744 valores. Intentemos diseñar una lista
capaz de almacenar todos estos resultados.

Primero, debes decidir qué tipo de datos sería adecuado para esta aplicación. En este
caso, sería mejor un float , ya que este termómetro puede medir la temperatura con
una precisión de 0.1 ℃.

Luego tomarás la decisión arbitraria de que las filas registrarán las lecturas cada hora
exactamente (por lo que la fila tendrá 24 elementos) y cada una de las filas se asignará a
un día del mes (supongamos que cada mes tiene 31 días, por lo que necesita 31 filas).
Aquí está el par apropiado de comprensiones( h es para las horas, d para el día):

temps = [[0.0 for h in range(24)] for d in range(31)]


Toda la matriz está llena de ceros ahora. Puede suponer que se actualiza
automáticamente utilizando agentes de hardware especiales. Lo que tienes que hacer es
esperar a que la matriz se llene con las mediciones.

Ahora es el momento de determinar la temperatura promedio mensual del mediodía.


Suma las 31 lecturas registradas al mediodía y divida la suma por 31. Puedes suponer
que la temperatura de medianoche se almacena primero. Aquí está el código:

temps = [[0.0 for h in range(24)] for d in range(31)]


#
# La matriz se actualiza aquí.
#

total = 0.0

for day in temps:


total += day[11]

average = total / 31

print("Temperatura promedio al mediodía:", average)

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, analiza el código:

temps = [[0.0 for h in range(24)] for d in range(31)]

#
# La matriz se actualiza aquí.
#

highest = -100.0

for day in temps:


for temp in day:
if temp > highest:
highest = temp

print("La temperatura más alta fue:", highest)

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

hot_days = 0

for day in temps:


if day[11] > 20.0:
hot_days += 1

print(hot_days, "fueron los días calurosos.")

3.7.1.5 Listas en aplicaciones avanzadas - Arreglos

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:

rooms = [[[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:

rooms[1][9][13] = True
y desocupar el segundo cuarto en el quinto piso ubicado en el primer edificio:

rooms[0][4][1] = False

Verifica si hay disponibilidad en el piso 15 del tercer edificio:

vacancy = 0

for room_number in range(20):


if not rooms[2][14][room_number]:
vacancy += 1

La variable vacancy 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!

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

3.7.1.6 RESUMEN DE LA SECCIÓN

Puntos Clave
1. La comprensión de listas te permite crear nuevas listas a partir de las existentes de una
manera concisa y elegante. La sintaxis de una comprensión de lista es la siguiente:

[expression for element in list if conditional]

El cual es un equivalente del siguiente código:

for element in list:


if conditional:
expression

Este es un ejemplo de una comprensión de lista: el código siguiente crea una lista de
cinco elementos con los primeros cinco números naturales elevados a la potencia de 3:

cubed = [num ** 3 for num in range(5)]


print(cubed) # outputs: [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(table)
print(table[0][0]) # outputs: ':('
print(table[0][3]) # outputs: ':)'

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)

cube = [[[':(', 'x', 'x'],


[':)', 'x', 'x'],
[':(', 'x', 'x']],

[[':)', 'x', 'x'],


[':(', 'x', 'x'],
[':)', 'x', 'x']],

[[':(', 'x', 'x'],


[':)', 'x', 'x'],
[':)', 'x', 'x']]]

print(cube)
print(cube[0][0][0]) # outputs: ':('
print(cube[2][2][0]) # outputs: ':)'

3.7.1.7 Módulo Completado

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

• 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 las rebanadas.
• La idea de los 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.

Fundamentos de Python 1: Módulo 4

Fundamentos de Python 1
Módulo 4
Funciones, Tuplas, Diccionarios, Exceptiones y Procesamiento de Datos
En este módulo, se cubrirán los siguientes temas:

• Estructuración de código y el concepto de función.


• Invocación de funciones y devolución de resultados de una función.
• Alcance de nombres y sombreado de variables.
• Tuplas y su propósito: construcción y uso de tuplas.
• Diccionarios y su propósito: construcción y uso de diccionarios;
• Introducción a las excepciones en Python.

4.1.1.1 Funciones

¿Por qué necesitamos funciones?


Hasta ahorita has implementado varias veces el uso de funciones, pero solo se han visto
algunas de sus ventajas. Solo se han invocado funciones para utilizarlas como
herramientas, con el fin de hacer la vida más 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, los cuales también son funciones, pero
declarados de una manera muy específica.

Ahora aprenderás a escribir tus propias funciones, y como utilizarlas. Escribiremos varias
de ellas juntos, desde muy sencillas hasta algo complejas. Se requerirá de tu
concentración y atención.

Muy a menudo ocurre que un cierto fragmento de código se repite muchas veces en un
programa. Se repite de manera literal o, con algunas modificaciones menores,
empleando algunas otras variables dentro del programa. También ocurre que un
programador ha comenzado a copiar y pegar ciertas partes del código en más de una
ocasión en el mismo programa.

Puede ser muy frustrante percatarse de repente que existe un error en el código copiado.
El programador tendrá que escarbar bastante para encontrar todos los lugares en el
código donde hay que corregir el error. Además, existe un gran riesgo de que las
correcciones produzcan errores adicionales.

Definamos la primer condición por la cual es una buena idea comenzar a escribir
funciones propias: si un fragmento de código comienza a aparecer en más de una
ocasión, considera la posibilidad de aislarlo en la forma de una función invocando la
función desde el lugar en el que originalmente se encontraba.
Puede suceder que el algoritmo que se desea implementar sea tan complejo que el
código comience a crecer de manera incontrolada y, de repente, ya no se puede navegar
por él tan fácilmente.

Se puede intentar solucionar este problema comentando el código, pero pronto te darás
cuenta que esto empeorará la situación - demasiados comentarios hacen que el código
sea más difícil de leer y entender. Algunos dicen que una función bien escrita debe ser
comprensible con tan solo una mirada.

Un buen desarrollador divide el código (o mejor dicho: el problema) en piezas aisladas,


y codifica cada una de ellas en la forma de una función.

Esto simplifica considerablemente el trabajo del programa, debido a que cada pieza se
codifica por separado y consecuentemente se prueba por separado. A este proceso se le
llama comúnmente descomposición.

Existe una segunda condición: si un fragmento de código se hace tan extenso que leerlo o
entenderlo se hace complicado, considera dividirlo pequeños problemas por separado e
implementa cada uno de ellos como una función independiente.

Esta descomposición continúa hasta que se obtiene un conjunto de funciones cortas,


fáciles de comprender y probar.

4.1.1.2 Funciones

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 más adelante) nos dará
como resultado el producto final.

Esto nos lleva directamente a la tercera condición: si se va a dividir el trabajo entre varios
programadores, se debe descomponer el problema para permitir que el producto sea
implementado como un conjunto de funciones escritas por separado empacadas juntas
en diferentes módulos.

¿De dónde provienen las funciones?


En general, las funciones provienen de al menos tres lugares:

• De Python mismo: varias funciones (como print() ) son una parte integral de
Python, y siempre están disponibles sin algún esfuerzo adicional del programador;
se les llama a estas funciones 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.
4.1.1.3 Escribiendo funciones

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 está
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 que tendrías que hacer si tu jefe
pidiera cambiar el mensaje para que fuese más 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 más 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())
4.1.1.4 Escribiendo funciones

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

Se necesita definirla. Aquí, la palabra definir es significativa.

Así es como se ve la definición más simple de una función:

def function_name():
function_body

• 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á message ‒ aquí está:

def message():
print("Ingresa un valor: ")

La función es muy sencilla, pero completamente utilizable. Se ha nombrado message ,


pero eso es opcional, tu puedes cambiarlo. Hagamos uso de ella.

El código ahora contiene la definición de la función:

def message():
print("Ingresa un valor: ")

print("Se comienza aquí.")


print("Se termina aquí.")

Nota: no se está utilizando la función, no se está invocando en el código.

Al correr el programa, se mostrará lo siguiente:

Se comienza aquí.
Se termina aquí.
salida
Esto significa que Python lee la definición de la función y la recuerda, pero no la ejecuta
sin tu permiso.

Se ha modificado el código, se ha insertado la invocación de la función entre los dos


mensajes:

def message():
print("Ingresa un valor: ")

print("Se comienza aquí.")


message()
print("Se termina aquí.")

La salida ahora se ve diferente:


Se comienza aquí.
Ingresa un valor:
Se termina aquí.
salida

Prueba el código, modifícalo, experimenta con él.

def my_function():
# cuerpo de la función

4.1.1.5 Analizando funciones

El funcionamiento de las funciones


Observa la imagen:

Intenta mostrarte 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 está definida más adelante, el lugar correcto para
definirla es antes de ser invocada.

Se ha insertado un error en el siguiente código - ¿Puedes notar la diferencia?

print("Se comienza aquí.")


message()
print("Se termina aquí.")

def message():
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 'message' is not defined

salida

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 message():
print("Ingresa un valor: ")

message = 1

El asignar un valor al nombre "message" causa que Python olvide su rol anterior. La
función con el nombre de message ya no estará disponible.
Afortunadamente, es posible combinar o mezclar el código con las funciones - no es
forzoso colocar todas las funciones al inicio del archivo fuente.

Observa el siguiente código:

print("Se comienza aquí.")

def message():
print("Ingresa un valor: ")

message()

print("Se termina aquí.")

Puede verse extraño, pero es completamente correcto, y funciona como se necesita.

Regresemos al ejemplo inicial para implementar la función de manera correcta:


def message():
print("Ingresa un valor: ")

message()
a = int(input())
message()
b = int(input())
message()
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.

4.1.1.6 RESUMEN DE LA SECCIÓN

Puntos Clave
1. Una función es un bloque de código que realiza una tarea especifica cuando la función
es llamada (invocada). Las funciones son útiles para hacer que el código sea reutilizable,
que este mejor organizado y más legible. Las funciones contienen parámetros y pueden
regresar valores.

2. Existen al menos cuatro tipos de funciones básicas en Python:

• Funciones integradas las cuales son partes importantes de Python (como lo es la


función print() ). Puedes ver una lista completa de las funciones integradas de
Python en la siguiente liga: https://docs.python.org/3/library/functions.html.
• También están las que se encuentran en módulos pre-instalados (se hablará
acerca de ellas en el curso Fundamentos de Python 2).
• Funciones definidas por el usuario las cuales son escritas por los programadores
para los programadores, puedes escribir tus propias funciones y utilizarlas
libremente en tu código.
• Las funciones lambda (aprenderás acerca de ellas en el curso Fundamentos de
Python 2).

3. Las funciones propias se pueden definir utilizando la palabra reservada def y con la
siguiente sintaxis:

def your_function(optional parameters):


# el cuerpo de la función

Se puede definir una función sin que haga uso de argumentos, por ejemplo:

def message(): # definiendo una función


print("Hello") # cuerpo de la función

message() # invocación de la función

También es posible definir funciones con argumentos, como la siguiente que contiene un
solo parámetro:

def hello(name): # definiendo una función


print("Hola,", name) # cuerpo de la función

name = input("Ingresa tu nombre: ")

hello(name) # invocación de la función

Se hablará más sobre las funciones parametrizadas en la siguiente sección. No te


preocupes.

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:

hi()

def hi():
print("¡Hola!")

Revisar

Se genera una excepción (la excepción NameError )

Ejercicio 3

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

def hi():
print("hola")

hi(5)

Revisar

Se genera una excepción (la excepción TypeError ) ‒ la función hi() no toma
argumentos

4.2.1.1 Cómo las funciones se comunican con su entorno

Funciones parametrizadas
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 más 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 function(parameter):

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

Funciones parametrizadas
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 más 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 function(parameter):

###

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 message(number):
###
Esta definición especifica que nuestra función opera con un solo parámetro con el
nombre de number . 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 message(number):
print("Ingresa un número:", number)

Se ha hecho buen uso del parámetro. Nota: No se le ha asignado al parámetro algún


valor. ¿Es correcto?

Si, lo es.

Un valor para el parámetro llegará del entorno de la función.

Recuerda: especificar uno o 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.

4.2.1.2 Cómo las funciones se comunican con su entorno

Funciones parametrizadas: continuación


Intenta ejecutar el código en el editor.

Esto es lo que aparecerá en consola:

TypeError: message() missing 1 required positional argument: 'number'


salida

Aquí está ya de manera correcta:

def message(number):
print("Ingresa un número:", number)

message(1)

De esta manera ya está correcto. El código producirá la siguiente salida:

Ingresa un número: 1
salida
¿Puedes ver cómo 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 number .

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 message(number):
print("Ingresa un número:", number)

number = 1234
message(1)
print(number)

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 number es una entidad completamente diferente de la variable


llamada number .

Esto significa que el código anterior producirá la siguiente salida:

Ingresa un número: 1
1234
salida
def message(number):
print("Ingresa un número:", number)

message()

4.2.1.3 Cómo las funciones se comunican con su entorno

Funciones parametrizadas: 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 message(what, number):


print("Ingresa", what, "número", number)

Esto significa que para invocar la función, se necesitan dos argumentos.

El primer parámetro va a contener el nombre del valor deseado.

Aquí está:

def message(what, number):


print("Ingresa", what, "número", number)

message("teléfono", 11)
message("precio", 5)
message("número", "número")

Esta es la salida del código anterior:

Ingresa teléfono número 11


Ingresa precio número 5
Ingresa número número número
salida

Ejecuta el código, modifícalo, agrega más parámetros y ve cómo esto afecta la salida.

def message(what, number):


print("Ingresa", what, "número", number)

# Invoca la función aquí.


4.2.1.4 Cómo las funciones se comunican con su entorno

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 my_function(a, b, c):


print(a, b, c)

my_function(1, 2, 3)

Nota: el paso de parámetros posicionales es usado de manera intuitiva por las personas
en muchas situaciones. Por ejemplo, es generalmente aceptado que cuando nos
presentamos mencionamos primero nuestro nombre(s) y después nuestro apellido, por
ejemplo, "Me llamo Juan Pérez."

Sin embargo, En Hungría se hace al revés.

Implementemos esa costumbre en Python. La siguiente función es utilizada para


presentar a alguien:

def introduction(first_name, last_name):


print("Hola, mi nombre es", first_name, last_name)

introduction("Luke", "Skywalker")
introduction("Jesse", "Quick")
introduction("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 introduction(first_name, last_name):


print("Hola, mi nombre es", first_name, last_name)

introduction("Skywalker", "Luke")
introduction("Quick", "Jesse")
introduction("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 ?

4.2.1.5 Cómo las funciones se comunican con su entorno

Paso de argumentos con palabra clave


Python ofrece otra manera de pasar argumentos, donde el significado del argumento
está definido por su nombre, no su posición, a esto se le denomina paso de argumentos
con palabra clave.

Observa el siguiente código:

def introduction(first_name, last_name):


print("Hola, mi nombre es", first_name, last_name)

introduction(first_name = "James", last_name = "Bond")


introduction(last_name = "Skywalker", first_name = "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 introduction(first_name, last_name):


print("Hola, mi nombre es", first_name, last_name)

introduction(surname="Skywalker", first_name="Luke")

Esto es lo que Python arrojará:


TypeError: introduction() got an unexpected keyword argument
'surname'

salida

Inténtalo tu mismo.
4.2.1.6 Cómo las funciones se comunican con su entorno

Combinar argumentos posicionales y de palabra 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 palabra clave.

Piénsalo por un momento y entenderás el porque.

Para mostrarte como funciona, se utilizará la siguiente función de tres parámetros:

def adding(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:

adding(1, 2, 3)

dará como salida:

1 + 2 + 3 = 6
salida

Hasta ahorita es un ejemplo solo de argumentos posicionales.

También, se puede reemplazar la invocación actual por una con palabras clave, como la
siguiente:

adding(c = 1, a = 2, b = 3)

El programa dará como salida lo siguiente:

2 + 3 + 1 = 6
salida

Ten presente el orden de los valores.

Ahora intentemos mezclar ambas formas.

Observa la siguiente invocación de la función:

adding(3, c = 1, b = 2)
Vamos a analizarla:

• El argumento ( 3 ) para el parámetro a es pasado utilizando la forma 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
salida

Sé 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 :

adding(3, a = 1, b = 2)

La respuesta de Python es:

TypeError: adding() got multiple values for argument 'a'


salida

Observa el siguiente código. Es un código completamente correcto y funcional, pero no


tiene mucho sentido:

adding(4, 3, c = 2)

Todo es correcto, pero el dejar solo un argumento con palabra clave es algo extraño -
¿Qué es lo que opinas?

def adding(a, b, c):


print(a, "+", b, "+", c, "=", a + b + c)

# Invoca la función aquí.

4.2.1.7 Cómo las funciones se comunican con su entorno

Funciones parametrizadas: más detalles


En ocasiones ocurre que algunos valores de ciertos argumentos son más utilizados que
otros. Dichos argumentos tienen valores predefinidos los cuales pueden ser
considerados cuando los argumentos correspondientes han sido omitidos.
Uno de los apellidos más comunes en Latinoamérica es González. Tomémoslo para el
ejemplo.

El valor por default para el parámetro se asigna de la siguiente manera:

def introduction(first_name, last_name="González"):


print("Hola, mi nombre es", first_name, last_name)

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:

introduction("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:

introduction("Enrique")

o así:

introduction(first_name="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
salida

Pruébalo.

Puedes hacerlo con más parámetros, si te resulta útil. Ambos parámetros tendrán sus
valores por default, observa el siguiente código:

def introduction(first_name="Juan", last_name="González"):


print("Hola, mi nombre es", first_name, last_name)

Esto hace que la siguiente invocación sea completamente valida:

introduction()
Y esta es la salida esperada:
Hola, mi nombre es Juan González
salida

Si solo se especifica un argumento de palabra clave, el restante tomará el valor por


default:

introduction(last_name="Rodríguez")

La salida es:

Hola, mi nombre es Juan Rodríguez


salida

Pruébalo.

Felicidades, has aprendido las técnicas básicas de comunicación con funciones.

def introduction(first_name, last_name="González"):


print("Hola, mi nombre es", first_name, last_name)

# Invoca la función aquí.

4.2.1.8 RESUMEN DE SECCIÓN

Puntos Clave
1. Se puede pasar información a las funciones utilizando parámetros. Las funciones
pueden tener tantos parámetros como sean necesarios.

Un ejemplo de una función con un parámetro:

def hi(name):
print("Hola,", name)

hi("Greg")

Un ejemplo de una función de dos parámetros:

def hi_all(name_1, name_2):


print("Hola,", name_2)
print("Hola,", name_1)

hi_all("Sebastián", "Konrad")

Un ejemplo de una función de tres parámetros:


def address(street, city, postal_code):
print("Tu dirección es:", street, city, postal_code)
s = input("Calle: ")
p_c = input("Código Postal: ")
c = input("Ciudad: ")

address(s, c, p_c)

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 subtra(a, b):
print(a - b)

subtra(5, 2) # salida: 3
subtra(2, 5) # salida: -3

#Ejemplo 2
def subtra(a, b):
print(a - b)

subtra(a=5, b=2) # salida: 3


subtra(b=2, a=5) # salida: 3

#Ejemplo 3
def subtra(a, b):
print(a - b)

subtra(5, b=2) # salida: 3


subtra(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 subtra(a, b):


print(a - b)

subtra(5, b=2) # salida: 3


subtra(a=5, 2) # Syntax Error

Python no lo ejecutará y marcará un error de sintaxis SyntaxError .


3. Se puede utilizar la técnica de argumentos con palabras clave para asignar
valores predefinidos a los argumentos:

def name(first_name, last_name="Pérez"):


print(first_name, last_name)

name("Andy") # salida: Andy Pérez


name("Bety", "Rodríguez") # salida: Bety Rodríguez (el argumento
de palabra clave es reemplazado por "Rodríguez")

Ejercicio 1

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

def intro(a="James Bond", b="Bond"):


print("Mi nombre es", b + ".", a + ".")

intro()

Revisar

Mi nombre es Bond. James Bond.

Ejercicio 2

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

def intro(a="James Bond", b="Bond"):


print("Mi nombre es", b + ".", a + ".")

intro(b="Sergio López")

Revisar

Mi nombre es Sergio López. James Bond

Ejercicio 3

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

def intro(a, b="Bond"):


print("Mi nombre es", b + ".", a + ".")

intro("Susan")

Revisar

Mi nombre es Bond. Susan.


Ejercicio 4

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

def add_numbers(a, b=2, c):


print(a + b + c)

add_numbers(a=1, c=3)

Revisar

SyntaxError - a non-default argument ( c ) follows a default argument ( b=2 )

4.3.1.1 Regresando el resultado de una función

Efectos y resultados: la instrucción return


Todas las funciones presentadas anteriormente tienen algún tipo de efecto: producen un
texto y lo envían a la consola.

Por supuesto, las funciones, al igual que las funciones matemáticas, pueden tener
resultados.

Para lograr que las funciones devuelvan un valor (pero no solo para ese propósito) se
utiliza la instrucción return (regresar o retornar).

Esta palabra nos da una idea completa de sus capacidades. Nota: es una palabra clave
reservada de Python.

La instrucción return tiene dos variantes diferentes: considerémoslas por separado.

return sin una expresión


La primera consiste en la palabra reservada en sí, sin nada que la siga.

Cuando se emplea dentro de una función, provoca la terminación inmediata de la


ejecución de la función, y un retorno instantáneo (de ahí el nombre) al punto de
invocación.

Nota: si una función no está destinada a producir un resultado, emplear la


instrucción return no es obligatorio, se ejecutará implícitamente al final de la función.

De cualquier manera, se puede emplear para terminar las actividades de una función,
antes de que el control llegue a la última línea de la función.
Consideremos la siguiente función:

def happy_new_year(wishes = True):


print("Tres...")
print("Dos...")
print("Uno...")
if not wishes:
return

print("¡Feliz año nuevo!")

Cuando se invoca sin ningún argumento:

happy_new_year()

La función produce un poco de ruido; la salida se verá así:

Tres...
Dos...
Uno...
¡Feliz año nuevo!
salida

Al proporcionar False como argumento:


happy_new_year(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...
salida

return con una expresión


La segunda variante de return está extendida con una expresión:

def function():
return expression

Existen 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 más) como el resultado de la función.

Si, este ejemplo es sencillo:


def boring_function():
return 123

x = boring_function()

print("La función boring_function ha devuelto su resultado. Es:", x)

El fragmento de código escribe el siguiente texto en la consola:

La función boring_function ha devuelto su resultado. Es: 123

Vamos a investigarlo.

Analiza la siguiente figura:

La instrucción return , enriquecida con la expresión (la expresión es muy simple aquí),
"transporta" el valor de la expresión al lugar donde se ha invocado la función.

El resultado se puede usar libremente aquí, por ejemplo, para ser asignado a una
variable.

También puede ignorarse por completo y perderse sin dejar rastro.

Ten en cuenta que no estamos siendo muy educados aquí: la función devuelve un valor y
lo ignoramos (no lo usamos de ninguna manera):

def boring_function():
print("'Modo aburrimiento' ON.")
return 123

print("¡Esta lección es interesante!")


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

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

4.3.1.2 Regresando el resultado de una función

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'

salida

Nota: None es una palabra clave reservada.

Solo existen 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í:

value = None
if value is None:
print("Lo siento, no contienes 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.

4.3.1.3 Regresando el resultado de una función

Algunas palabras acerca de None: continuación


Echa un vistazo al código en el editor.

Es obvio que la función strangeFunction retorna True cuando su argumento es par.

¿Qué es lo que retorna de otra manera?

Podemos usar el siguiente código para verificarlo:

print(strange_function(2))
print(strange_function(1))

Esto es lo que vemos en la consola:

True
None
salida

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.

def strange_function(n):
if(n % 2 == 0):
return True

4.3.1.4 Regresando el resultado de una función

Efectos y resultados: listas y funciones


Existen dos preguntas adicionales que deben responderse aquí.
La primera 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 list_sum(lst):
s = 0

for elem in lst:


s += elem

return s

y se invoca así:

print(list_sum([5, 4, 3]))

Retorna 12 como resultado, pero habrá problemas si la invocas de esta manera riesgosa:

print(list_sum(5))

La respuesta de Python será la siguiente:

TypeError: 'int' object is not iterable


salida

Esto se debe al hecho de que el bucle for no puede iterar un solo valor entero.

def list_sum(lst):
s = 0

for elem in lst:


s += elem

return s

4.3.1.5 Regresando el resultado de una función

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]
salida

Ahora puedes escribir funciones con y sin resultados.

Vamos a profundizar un poco más en los problemas relacionados con las variables en las
funciones. Esto es esencial para crear funciones efectivas y seguras.

def strange_list_fun(n):
strange_list = []

for i in range(0, n):


strange_list.insert(0, i)

return strange_list

print(strange_list_fun(5))

4.3.1.6 LABORATORIO: Un año bisiesto: escribiendo tus propias


funciones

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 instrucción 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 si no lo es.

Parte del esqueleto de la función ya está en el editor.

Nota: también hemos preparado un breve código de prueba, que puedes utilizar para
probar tu función.

El código utiliza dos listas: una con los datos de prueba y la otra con los resultados
esperados. El código te dirá si alguno de tus resultados no es válido.

def is_year_leap(year):
#
# Escribe tu código aquí.
#

test_data = [1900, 2000, 2016, 1987]


test_results = [False, True, True, False]
for i in range(len(test_data)):
yr = test_data[i]
print(yr,"->",end="")
result = is_year_leap(yr)
if result == test_results[i]:
print("OK")
else:
print("Fallido")

4.3.1.7 LABORATORIO: ¿Cuántos días?: escribiendo y utilizando tus


propias funciones
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 instrucción return.
• Utilizar las funciones propias del estudiante.

Escenario
Tu tarea es escribir y probar una función que toma dos argumentos (un año y un mes) y
devuelve el número de días del mes/año dado (mientras que solo febrero es sensible al
valor year , tu función debería ser universal).

La parte inicial de la función está lista. Ahora, haz que la función devuelva None si los
argumentos no tienen sentido.

Por supuesto, puedes (y debes) utilizar la función previamente escrita y probada


(LABORATORIO 4.1.3.6). Puede ser muy útil. Te recomendamos que utilices una lista con
los meses. Puedes 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 is_year_leap(year):
#
# Tu código del LABORATORIO 4.3.6.
#

def days_in_month(year, month):


#
# Escribe tu código aquí.
#

test_years = [1900, 2000, 2016, 1987]


test_months = [2, 2, 1, 11]
test_results = [28, 29, 31, 30]
for i in range(len(test_years)):
yr = test_years[i]
mo = test_months[i]
print(yr, mo, "->", end="")
result = days_in_month(yr, mo)
if result == test_results[i]:
print("OK")
else:
print("Fallido")

4.3.1.8 LABORATORIO: Día del año: escribiendo y utilizando tus


propias funciones

LABORATORIO

Tiempo Estimado
20-30 minutos

Nivel de Dificultad
Medio

Requisitos Previos
LABORATORIO 4.1.3.6
LABORATORIO 4.1.3.7

Objetivos
Familiarizar al estudiante con:

• Proyectar y escribir funciones con parámetros.


• Utilizar la sentencia return.
• Construir un conjunto de funciones de utilidad.
• Utilizar las funciones propias del estudiante.

Escenario
Tu tarea es escribir y probar una función que toma tres argumentos (un año, un mes y un
día del mes) y devuelve el día correspondiente del año, o devuelve None si cualquiera de
los argumentos no es válido.

Debes utilizar las funciones previamente escritas y probadas. Agrega algunos casos de
prueba al código. Esta prueba es solo el comienzo.

def is_year_leap(year):
#
# Tu código del LABORATORIO 4.3.1.6.
#
def days_in_month(year, month):
#
# Tu código del LABORATORIO 4.3.1.7.
#

def day_of_year(year, month, day):


#
# Escribe tu código nuevo aquí.
#

print(day_of_year(2000, 12, 31))

4.3.1.9 LABORATORIO: Números primos: ¿Cómo encontrarlos?


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 emplear funciones.

Escenario
Un número natural es primo si es mayor que 1 y no tiene divisores más que 1 y si mismo.

¿Complicado? De ningúna manera. Por ejemplo, 8 no es un número primo, ya que puedes


dividirlo entre 2 y 4 (no podemos usar divisores iguales a 1 y 8, ya que la definición lo
prohíbe).

Por otra parte, 7 es un número primo, ya que no podemos encontrar ningún divisor para
el.

Tu tarea es escribir una función que verifique si un número es primo o no.

La función:

• Se llama is_prime .
• 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 lo mismo 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 is_prime(num):
#
# Escribe tu código aquí.
#

for i in range(1, 20):


if is_prime(i + 1):
print(i + 1, end=" ")
print()

4.3.1.10 LAB: Convirtiendo el consumo de combustible

LABORATORIO

Tiempo Estimado
10-15 minutos

Nivel de Dificultad
Fácil

Objetivos
• Mejorar las habilidades del estudiante para definir, utilizar y probar funciones.
Escenario
El consumo de combustible de un automóvil se puede expresar de muchas maneras
diferentes. Por ejemplo, en Europa, se muestra como la cantidad de combustible
consumido por cada 100 kilómetros.

En los EE. UU., se muestra como la cantidad de millas recorridas por un automóvil con un
galón de combustible.

Tu tarea es escribir un par de funciones que conviertan l/100km a mpg (milas por galón),
y viceversa.

Las funciones:

• Se
llaman liters_100km_to_miles_gallon y miles_gallon_to_liters_100km re
spectivamente.
• 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.

Salida esperada:

60.31143162393162
31.36194444444444
23.52145833333333
3.9007393587617467
7.490910297239916
10.009131205673757

def liters_100km_to_miles_gallon(liters):
#
# Escribe tu código aquí.
#

def miles_gallon_to_liters_100km(miles):
#
# Escribe tu código aquí.
#

print(liters_100km_to_miles_gallon(3.9))
print(liters_100km_to_miles_gallon(7.5))
print(liters_100km_to_miles_gallon(10.))
print(miles_gallon_to_liters_100km(60.3))
print(miles_gallon_to_liters_100km(31.4))
print(miles_gallon_to_liters_100km(23.5))

4.3.1.11 RESUMEN DE SECCIÓN

Puntos Clave
1. Puedes emplear la palabra clave reservada 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 wishes():
return "¡Felíz Cumpleaños!"

w = wishes()

print(w) # salida:¡Felíz Cumpleaños!

Observa la diferencia en la salida en los siguientes dos ejemplos:

# Ejemplo 1

def wishes():
print("Mis deseos")
return "Felíz Cumpleaños"

wishes() # salida: Mis deseos

# Ejemplo 2
def wishes():
print("Mis deseos")
return "Felíz Cumpleaños"
print(wishes())

# salida: Mis deseos


# Felíz Cumpleaños

3. Puedes usar una lista como argumento de una función, por ejemplo:

def hi_everybody(my_list):
for name in my_list:
print("Hola,", name)

hi_everybody(["Adán", "Juan", "Lucía"])

4. Una lista también puede ser un resultado de función, por ejemplo:

def create_list(n):
my_list = []
for i in range(n):
my_list.append(i)
return my_list

print(create_list(5))

Ejercicio 1
¿Cuál es la salida del siguiente fragmento de código?
def hi():
return
print("¡Hola!")

hi()

Revisar

La función devolverá un valor None implícito

Ejercicio 2
¿Cuál es la salida del siguiente fragmento de código?
def is_int(data):
if type(data) == int:
return True
elif type(data) == float:
return False

print(is_int(5))
print(is_int(5.0))
print(is_int("5"))
Revisar

True
False
None

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

print(even_num_lst(11))

Revisar

[0, 2, 4, 6, 8, 10]

Ejercicio 4
¿Cuál es la salida del siguiente fragmento de código?
def list_updater(lst):
upd_list = []
for elem in lst:
elem **= 2
upd_list.append(elem)
return upd_list

foo = [1, 2, 3, 4, 5]
print(list_updater(foo))

Revisar

[1, 4, 9, 16, 25]

4.4.1.1 Los Alcances en Python

Las funciones y sus alcances


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 sí. El parámetro es
inaccesible fuera de la función.
Vamos a revisarlo. Observa el código en el editor. ¿Qué ocurrirá cuando se ejecute?

El programa no correrá. El mensaje de error dirá:

NameError: name 'x' is not defined


salida

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 scope_test():
x = 123

scope_test()
print(x)

4.4.1.2 Los Alcances en Python

Las funciones y sus alcances: 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
salida

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 my_function():
var = 2
print("¿Conozco a la variable?", var)
var = 1
my_function()
print(var)

El resultado ha cambiado también, el código arroja una salida con una ligera diferencia:

¿Conozco a la variable? 2
1
salida

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

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 my_function():
print("¿Conozco a la variable?", var)

var = 1
my_function()
print(var)

4.4.1.3 Alcances en Python | La palabra reservada: global

Las funciones y sus alcances: la palabra clave


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 incómodo.

Afortunadamente, la respuesta es no.


Existe un método especial en Python el cual puede extender el alcance de una variable
incluyendo el cuerpo de las funciones para poder no solo leer los valores de las variables
sino también modificarlos.

Este efecto es causado por la palabra clave 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
salida

Esto debe de ser suficiente evidencia para mostrar lo que la palabra clave
reservada global puede hacer.

def my_function():
global var
var = 2
print("¿Conozco a aquella variable?", var)

var = 1
my_function()
print(var)

4.4.1.4 Los Alcances en Python

Como interactúa la función con sus argumentos


Ahora descubramos como la función interactúa con sus argumentos.
El código en el 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 recibí 1
Ahora tengo 2
1
salida

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 rebanadas de listas en lugar de asignar la lista entera?)

El siguiente ejemplo arrojará luz sobre el asunto:

def my_function(my_list_1):
print("Print #1:", my_list_1)
print("Print #2:", my_list_2)
my_list_1 = [0, 1]
print("Print #3:", my_list_1)
print("Print #4:", my_list_2)

my_list_2 = [2, 3]
my_function(my_list_2)
print("Print #5:", my_list_2)

La salida del código es:

Print #1: [2, 3]


Print #2: [2, 3]
Print #3: [0, 1]
Print #4: [2, 3]
Print #5: [2, 3]
salida

Parece ser que se sigue aplicando la misma regla.


Finalmente, la diferencia se puede observar en el siguiente ejemplo:

def my_function(my_list_1):
print("Print #1:", my_list_1)
print("Print #2:", my_list_2)
del my_list_1[0] # Presta atención a esta línea.
print("Print #3:", my_list_1)
print("Print #4:", my_list_2)

my_list_2 = [2, 3]
my_function(my_list_2)
print("Print #5:", my_list_2)

No se modifica el valor del parámetro my_list_1 (ya se sabe que no afectará el


argumento), en lugar de ello se modificará la lista identificada por él.

El resultado puede ser sorprendente. Ejecuta el código y verifícalo:

Print #1: [2, 3]


Print #2: [2, 3]
Print #3: [3]
Print #4: [3]
Print #5: [3]
salida

¿Lo puedes explicar?

Intentémoslo:

• Si el argumento es una lista, el cambiar el valor del parámetro correspondiente no


afecta la lista (Recuerda: las variables que contienen listas son almacenadas de
manera diferente que las escalares).
• Pero si se modifica la lista identificada por el parámetro (Nota: ¡La lista, no el
parámetro!), la lista reflejará el cambio.

Es tiempo de escribir algunos ejemplos de funciones. Lo harás en la siguiente sección.

def my_function(n):
print("Yo recibí", n)
n += 1
print("Ahora tengo", n)

var = 1
my_function(var)
print(var)
4.4.1.5 RESUMEN DE SECCIÓN

Puntos Clave
1. Una variable que existe fuera de una función tiene alcance dentro del cuerpo de la
función. (Ejemplo 1) al menos que la función defina una variable con el mismo nombre.
(Ejemplo 2, y Ejemplo 3), por ejemplo:

Ejemplo 1:

var = 2

def mult_by_var(x):
return x * var

print(mult_by_var(7)) # salida: 14

Ejemplo 2:

def mult(x):
var = 5
return x * var

print(mult(7)) # salida: 35

Ejemplo 3:

def mult(x):
var = 7
return x * var

var = 3
print(mult(7)) # salida: 49

2. Una variable que existe dentro de una función tiene un alcance solo dentro del cuerpo
de la función (Ejemplo 4), por ejemplo:

Ejemplo 4:

def adding(x):
var = 7
return x + var

print(adding(4)) # salida: 11
print(var) # NameError
3. Se puede emplear la palabra clave reservada global seguida por el nombre de una
variable para que el alcance de la variable sea global, por ejemplo:

var = 2
print(var) # salida: 2

def return_var():
global var
var = 5
return var

print(return_var()) # salida: 5

print(var) # salida: 5

Ejercicio 1

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

def message():
alt = 1
print("¡Hola, mundo!")

print(alt)

Revisar

Se arrojará una excepción NameError ( NameError: name 'alt' is not defined )

Ejercicio 2

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

a = 1

def fun():
a = 2
print(a)

fun()
print(a)

Revisar
2
1
Ejercicio 3

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

a = 1

def fun():
global a
a = 2
print(a)

fun()
a = 3
print(a)

Revisar
2
3

Ejercicio 4

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

a = 1

def fun():
global a
a = 2
print(a)

a = 3
fun()
print(a)

Revisar
2
2

4.5.1.1 Creando funciones con dos parámetros

Funciones Simples: Calcular el IMC


Definamos una función que calcula el Índice de Masa Corporal (IMC).
Como puedes observar, la formula ocupa dos valores:

• peso (originalmente en kilogramos)


• altura (originalmente en metros)

La nueva función tendrá dos parámetros. Su nombre será bmi , pero si prefieres utilizar
otro nombre, adelante.

Codifiquemos la función.

def bmi(weight, height):

return weight / height ** 2

print(bmi(52.5, 1.65))

El resultado del ejemplo anterior es el siguiente:

19.283746556473833
salida

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 bmi(weight, height):


return weight / height ** 2

print(bmi(52.5, 1.65))

4.5.1.2 Creando funciones con dos parámetros

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.
Primero, se asegura que los datos que sean ingresados sean correctos, de lo contrario la
salida será:

None
salida

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 más
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á lb_to_kg :

def lb_to_kg(lb):
return lb * 0.45359237

print(lb_to_kg(1))

El resultado de la prueba es el siguiente:

0.45359237
salida

Haremos lo mismo ahora con los pies y pulgadas: 1 ft = 0.3048 m , y 1 in = 2.54 cm


= 0.0254 m .

La función se llamará ft_and_inch_to_m :

def ft_and_inch_to_m(ft, inch):

return ft * 0.3048 + inch * 0.0254

print(ft_and_inch_to_m(1, 1))

El resultado de una prueba rápida es:

0.3302
salida
Resulta como esperado.

Nota: queríamos nombrar el segundo parámetro solo in , no inch , pero no pudimos.


¿Sabes por qué?

in es una palabra clave reservada de Python ‒ no se puede usar como nombre.

Vamos a convertir seis pies a metros:

print(ft_and_inch_to_m(6, 0))

Esta es la salida:

1.8288000000000002
salida

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 ft_and_inch_to_m(ft, inch = 0.0):


return ft * 0.3048 + inch * 0.0254

print(ft_and_inch_to_m(6))

Ahora el parámetro inch tiene como valor predeterminado el 0.0 .


El código produce la siguiente salida, esto es lo que se esperaba:

1.8288000000000002
salida

Finalmente, el código es capaz de responder a la pregunta: ¿Cuál es el IMC de una


persona que tiene 5'7" de altura y un peso de 176 lbs?

Este es el código que hemos construido:

def ft_and_inch_to_m(ft, inch = 0.0):


return ft * 0.3048 + inch * 0.0254

def lb_to_kg(lb):
return lb * 0.45359237

def bmi(weight, height):


if height < 1.0 or height > 2.5 or weight < 20 or weight > 200:
return None

return weight / height ** 2


print(bmi(weight = lb_to_kg(176), height = ft_and_inch_to_m(5, 7)))

La respuesta es:

27.565214082533313
salida

Ejecuta el código y pruébalo.

def bmi(weight, height):


if height < 1.0 or height > 2.5 or \
weight < 20 or weight > 200:
return None

return weight / height ** 2

print(bmi(352.5, 1.65))

4.5.1.3 Creando funciones con tres parámetros

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, is_a_triangle es un buen nombre para dicha función.

Observa el código en el editor. Ahí se encuentra la función. Ejecuta el programa.

Parece que funciona perfectamente: estos son los resultados:

True
False
salida

¿Se podrá hacer más compacta?. Parece tener demasiadas palabras.

Esta es la versión más compacta:

def is_a_triangle(a, b, c):


if a + b <= c or b + c <= a or c + a <= b:
return False
return True

print(is_a_triangle(1, 1, 1))
print(is_a_triangle(1, 1, 3))

¿Se puede compactar aun más?

Por supuesto, observa:

def is_a_triangle(a, b, c):


return a + b > c and b + c > a and c + a > b

print(is_a_triangle(1, 1, 1))
print(is_a_triangle(1, 1, 3))

Se ha negado la condición (se invirtieron los operadores relacionales y se reemplazaron


los or s con and s, obteniendo una expresión universal para probar triángulos).

Coloquemos la función en un programa más grande. Se le pedirá al usuario los tres


valores y se hará uso de la función.

def is_a_triangle(a, b, c):


if a + b <= c:
return False
if b + c <= a:
return False
if c + a <= b:
return False
return True

print(is_a_triangle(1, 1, 1))
print(is_a_triangle(1, 1, 3))

4.5.1.4 Creando funciones: probando triángulos

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 is_a_triangle . 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 = a 2 + b 2

¿Cómo saber cual de los tres lados es la hipotenusa?

La hipotenusa es el lado más largo.

Aquí esta el código:


def is_a_triangle(a, b, c):
return a + b > c and b + c > a and c + a > b

def is_a_right_triangle(a, b, c):


if not is_a_triangle(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(is_a_right_triangle(5, 3, 4))
print(is_a_right_triangle(1, 3, 4))
Observa como se establece la relación entre la hipotenusa y los dos catetos. Se eligió el
lado más largo y se aplico el Teorema de Pitágoras para verificar que todo estuviese en
orden. Esto requiere tres revisiones en total.

def is_a_triangle(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 is_a_triangle(a, b, c):
print('Si, si puede ser un triángulo.')
else:
print('No, no puede ser un triángulo.')

4.5.1.5 Creando funciones: triángulos rectángulos

Algunas funciones simples: evaluando el área de un


triángulo
También es posible evaluar el área 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 is_a_triangle(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 area_of_triangle(a, b, c):
if not is_a_triangle(a, b, c):
return None
return heron(a, b, c)

print(area_of_triangle(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 área debe ser igual a 0.5.

Es extraño pero el código produce la siguiente salida:

0.49999999999999983
salida

Es muy cercano a 0.5, pero no es exactamente 0.5,¿Qué significa?, ¿Es un error?

No, no lo es. Son solo los cálculos de valores punto flotantes. Pronto se discutirá el tema.

def is_a_triangle(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 is_a_triangle(a, b, c):
print('Si, si puede ser un triángulo.')
else:
print('No, no puede ser un triángulo.')

4.5.1.6 Creando funciones: factoriales

Algunas funciones simples: factoriales


La siguiente función a definir calcula factoriales. ¿Recuerdas cómo se calcula un factorial?

0! = 1 (¡Si!, es verdad.)
1! = 1
2! = 1 * 2
3! = 1 * 2 * 3
4! = 1 * 2 * 3 * 4
:
:
n! = 1 * 2 ** 3 * 4 * ... * n-1 * n
Se expresa con un signo de exclamación, y es igual al producto de todos los números
naturales previos al argumento o número dado.

Escribamos el código. Creemos una función con el nombre factorial_function . Aquí


esta el código:

def factorial_function(n):
if n < 0:
return None
if n < 2:
return 1

product = 1
for i in range(2, n + 1):
product *= i
return product

for n in range(1, 6): # probando


print(n, factorial_function(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 del código de prueba:

1 1
2 2
3 6
4 24
5 120
salida

4.5.1.7 Creando funciones: Fibonacci

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 número 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:


fib_1 = 1
fib_2 = 1
fib_3 = 1 + 1 = 2
fib_4 = 1 + 2 = 3
fib_5 = 2 + 3 = 5
fib_6 = 3 + 5 = 8
fib_7 = 5 + 8 = 13

¿Que opinas acerca de implementarlo como una función?

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

def fib(n):
if n < 1:
return None
if n < 3:
return 1

elem_1 = elem_2 = 1
the_sum = 0
for i in range(3, n + 1):
the_sum = elem_1 + elem_2
elem_1, elem_2 = elem_2, the_sum
return the_sum

for n in range(1, 10): # probando


print(n, "->", fib(n))

Analiza el codigo del bucle for cuidadosamente, descifra como se mueven las
variables elem_1 y elem_2 a través de los números subsecuentes de la serie Fibonacci.

Al probar el código, se generan los siguientes resultados:

1 -> 1
2 -> 1
3 -> 2
4 -> 3
5 -> 5
6 -> 8
7 -> 13
8 -> 21
9 -> 34
salida

4.5.1.8 Creando funciones: recursividad


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

Este termino puede describir muchos conceptos distintos, pero uno de ellos, hace
referencia a la programación computacional.

Aquí, la recursividad es una técnica donde una función se invoca a si misma.

Tanto el factorial como la serie Fibonacci, son las mejores opciones para ilustrar este
fenómeno.

La serie de Fibonacci es un claro ejemplo de recursividad. Ya te dijimos que:

Fibi = Fibi-1 + Fibi-2

El número i 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 más
corto y claro.

La segunda versión de la función fib() hace uso directo de la recursividad:

def fib(n):
if n < 1:
return None
if n < 3:
return 1
return fib(n - 1) + fib(n - 2)

El código es mucho más claro ahora.

¿Pero es realmente seguro?, ¿Implica algún riesgo?

Si, existe algo de riesgo. Si no se considera una condición que detenga las invocaciones
recursivas, el programa puede entrar en un bucle infinito. Se debe ser cuidadoso.

El factorial también tiene un lado recursivo. Observa:

n! = 1 × 2 × 3 × ... × n-1 × n

Es obvio que:

1 × 2 × 3 × ... × n-1 = (n-1)!

Entonces, finalmente, el resultado es:

n! = (n-1)! × n
Esto se empleará en nuestra nueva solución.

Aquí esta:

def factorial_function(n):
if n < 0:
return None
if n < 2:
return 1
return n * factorial_function(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):
if n < 1:
return None
if n < 3:
return 1

elem_1 = elem_2 = 1
the_sum = 0
for i in range(3, n + 1):
the_sum = elem_1 + elem_2
elem_1, elem_2 = elem_2, the_sum
return the_sum

for n in range(1, 10):


print(n, "->", fib(n))

4.5.1.9 RESUMEN DE SECCIÓN

Puntos Clave
1. Una función puede invocar otras funciones o incluso a sí misma. Cuando una función
se invoca a si misma, se le conoce como recursividad, y la función que se invoca a si
misma y contiene una condición de terminación (la cual le dice a la función que ya no siga
invocándose a si misma) es llamada una función recursiva.

2. Se pueden emplear funciones recursivas en Python para crear funciones limpias,


elegantes, y dividir el código en trozos más pequeños. Sin embargo, se debe tener mucho
cuidado ya que es muy fácil cometer un error y crear una función la cual nunca
termine. También se debe considerar que las funciones recursivas consumen mucha
memoria, y por lo tanto pueden ser en ocasiones ineficientes.

Al emplear la recursividad, se deben de tomar en cuenta tanto sus ventajas como


desventajas.

La función factorial es un ejemplo clásico de como se puede implementar el concepto de


recursividad:

# Implementación recursiva de la función factorial.

def factorial(n):
if n == 1: # El caso base (condición de terminación).
return 1
else:
return n * factorial(n - 1)

print(factorial(4)) # 4 * 3 * 2 * 1 = 24

Ejercicio 1

¿Qué ocurrirá al intentar ejecutar el siguiente fragmento de código y por qué?

def factorial(n):
return n * factorial(n - 1)

print(factorial(4))
Revisar

La función no tiene una condición de terminación, por lo tanto Python arrojara una
excepción ( RecursionError: maximum recursion depth exceeded )

Ejercicio 2

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

def fun(a):
if a > 30:
return 3
else:
return a + fun(a + 3)

print(fun(25))

Revisar

56
4.6.1.1 Tuplas y diccionarios

Tipos de secuencias y mutabilidad


Antes de comenzar a hablar acerca de tuplas y diccionarios, se deben introducir dos
conceptos importantes: tipos de secuencia y mutabilidad.

Un tipo de secuencia es un tipo de dato en Python el cual es capaz de almacenar más de


un valor (o ninguno si la secuencia esta vacía), los cuales pueden ser secuencialmente (de
ahí el nombre) examinados, elemento por elemento.

Debido a que el bucle for es una herramienta especialmente diseñada para iterar a
través de las secuencias, podemos definirlas de la siguiente manera: una secuencia es un
tipo de dato que puede ser escaneado por el bucle for .

Hasta ahora, has trabajado con una secuencia en Python, la lista. La lista es un clásico
ejemplo de una secuencia de Python. Aunque existen otras secuencias dignas de
mencionar, las cuales se presentaran a continuación.

La segunda noción - la mutabilidad - es una propiedad de cualquier tipo de dato en


Python que describe su disponibilidad para poder cambiar libremente durante la
ejecución de un programa. Existen dos tipos de datos en Python: mutables e inmutables.

Los datos mutables pueden ser actualizados libremente en cualquier momento, a esta
operación se le denomina "in situ".

In situ es una expresión en Latín que se traduce literalmente como en posición, en el


lugar o momento. Por ejemplo, la siguiente instrucción modifica los datos "in situ":

list.append(1)

Los datos inmutables no pueden ser modificados de esta manera.

Imagina que una lista solo puede ser asignada y leída. No podrías adjuntar ni remover un
elemento de la lista. Si se agrega un elemento al final de la lista provocaría que la lista se
cree desde cero.

Se tendría que crear una lista completamente nueva, la cual contenga los elementos ya
existentes más 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:

tuple_1 = (1, 2, 4, 8)
tuple_2 = 1., .5, .25, .125

Se definieron dos tuplas, ambas contienen cuatro elementos.

A continuación se imprimen en consola:

tuple_1 = (1, 2, 4, 8)
tuple_2 = 1., .5, .25, .125

print(tuple_1)
print(tuple_2)

Esto es lo que se muestra en consola:

(1, 2, 4, 8)
(1.0, 0.5, 0.25, 0.125)
salida

Nota: cada elemento de una tupla puede ser de distinto tipo (punto flotante, entero,
cadena, o cualquier otro tipo de dato).

¿Cómo crear una tupla?


¿Es posible crear una tupla vacía? Si, solo se necesitan unos paréntesis:

empty_tuple = ()

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:

one_element_tuple_1 = (1, )
one_element_tuple_2 = 1.,

El quitar las comas no arruinará el programa en el sentido sintáctico, pero serán dos
variables, no tuplas.
4.6.1.2 Tuplas y diccionarios

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

El programa debe de generar la siguiente salida, ejecútalo y comprueba:

1
1000
(10, 100, 1000)
(1, 10)
1
10
100
1000
salida

Las similitudes pueden ser engañosas - no intentes modificar el contenido de la tupla ¡No
es una lista!

Todas estas instrucciones (con excepción de primera) causarán un error de ejecución :

my_tuple = (1, 10, 100, 1000)

my_tuple.append(10000)
del my_tuple[0]
my_tuple[1] = -10

Este es el mensaje que Python arrojará en la ventana de consola:

AttributeError: 'tuple' object has no attribute 'append'

salida

my_tuple = (1, 10, 100, 1000)

print(my_tuple[0])
print(my_tuple[-1])
print(my_tuple[1:])
print(my_tuple[:-2])

for elem in my_tuple:


print(elem)
4.6.1.3 Tuplas y diccionarios

¿Cómo utilizar una tupla?: continuación


¿Qué más pueden hacer las tuplas?

• La función len() acepta tuplas, y regresa el número 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
(1, 10, 100, 1000, 10000)
(1, 10, 100, 1, 10, 100, 1, 10, 100)
True
True
salida

Una de las propiedades de las tuplas más útiles es que pueden aparecer en el lado
izquierdo del operador de asignación. Este fenómeno ya se vio con anterioridad, cuando
fue necesario encontrar una manera de intercambiar los valores entre dos variables.

Observa el siguiente fragmento de código:

var = 123

t1 = (1, )
t2 = (2, )
t3 = (3, var)

t1, t2, t3 = t2, t3, t1

print(t1, t2, t3)

Muestra tres tuplas interactuando en efecto, los valores almacenados en ellas "circulan"
entre ellas. t1 se convierte en t2 , t2 se convierte en t3 , y t3 se convierte en t1 .

Nota: el ejemplo presenta un importante hecho mas: los elementos de una tupla pueden
ser variables, no solo literales. Además, pueden ser expresiones si se encuentran en el
lado derecho del operador de asignación.

my_tuple = (1, 10, 100)


t1 = my_tuple + (1000, 10000)
t2 = my_tuple * 3

print(len(t2))
print(t1)
print(t2)
print(10 in my_tuple)
print(-10 not in my_tuple)

4.6.1.4 Tuplas y diccionarios

¿Qué es un diccionario?
El diccionario es otro tipo de estructura de datos de Python. No es una secuencia (pero
puede adaptarse fácilmente a un procesamiento secuencial) y además es mutable.

Para explicar lo que es un diccionario en Python, es importante comprender de manera


literal lo que es un diccionario.

Un diccionario en Python funciona de la misma manera que un diccionario bilingüe. Por


ejemplo, se tiene la palabra en español "gato" y se necesita su equivalente en francés. Lo
que se haría es buscar en el diccionario para encontrar la palabra "gato". Eventualmente
la encontrarás, y sabrás que la palabra equivalente en francés es "chat".

En el mundo de Python, la palabra que se esta buscando se denomina clave(key) . La


palabra que se obtiene del diccionario es denominada valor .

Esto significa que un diccionario es un conjunto de pares de claves y valores. Nota:

• Cada clave debe de ser única. No es posible tener una clave duplicada.
• Una clave puede ser un tipo de dato de cualquier tipo: puede ser un número
(entero o flotante), o incluso una cadena.
• Un diccionario no es una lista. Una lista contiene un conjunto de valores
numerados, mientras que un diccionario almacena pares de valores.
• La función len() aplica también para los diccionarios, regresa la cantidad de
pares (clave-valor) en el diccionario.
• Un diccionario es una herramienta de un solo sentido. Si fuese un diccionario
español-francés, podríamos buscar en español para encontrar su contraparte en
francés más 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:

dictionary = {"gato" : "chat", "perro" : "chien", "caballo" :


"cheval"}
phone_numbers = {'jefe': 5551234567, 'Suzy': 22657854310}
empty_dictionary = {}

print(dictionary)
print(phone_numbers)
print(empty_dictionary)

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, 'jefe': 5551234567}
{}
salida

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

4.6.1.5 Tuplas y diccionarios

¿Cómo utilizar un diccionario?


Si deseas obtener cualquiera de los valores, debes de proporcionar una clave válida:

print(dictionary['gato'])
print(phone_numbers['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
salida

Ahora algo muy importante: No se puede utilizar una clave que no exista. Hacer algo
como lo siguiente:

print(phone_numbers['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:

dictionary = {"gato" : "chat", "perro" : "chien", "caballo" :


"cheval"}
wods = ['gato', 'león', 'caballo']

for word in words:


if word in dictionary:
print(word, "->", dictionary[word])
else:
print(word, "no está en el diccionario")

La salida del código es la siguiente:

gato -> chat


león no está en el diccionario
caballo -> cheval
salida

NOTA

Cuando escribes una expresión grande o larga, puede ser una buena idea mantenerla
alineada verticalmente. Así es como puede hacer que el código sea más legible y más
amigable para el programador, por ejemplo:

# Ejemplo 1:
dictionary = {
"gato": "chat",
"perro": "chien",
"caballo": "cheval"
}

# Ejemplo 2:
phone_numbers = {'jefe': 5551234567,
'Suzy': 22657854310
}

Este tipo de formato se llama sangría francesa.

dictionary = {"gato" : "chat", "perro" : "chien", "caballo" :


"cheval"}
phone_numbers = {'jefe' : 5551234567, 'Suzy' : 22657854310}
empty_dictionary = {}

# Imprimir valores aquí.

4.6.1.6 Tuplas y diccionarios - métodos

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

dictionary = {"gato" : "chat", "perro" : "chien", "caballo" :


"cheval"}

for key in dictionary.keys():


print(key, "->", dictionary[key])

El código produce la siguiente salida:


gato -> chat
perro -> chien
caballo -> cheval
salida

La función sorted()
¿Deseas que la salida este ordenada? Solo hay que agregar al bucle for lo siguiente:

for key in sorted(dictionary.keys()):

La función sorted() hará su mejor esfuerzo y la salida será la siguiente:

caballo -> cheval


gato -> chat
perro -> chien
salida

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

for key in dictionary.keys():


print(key, "->", dictionary[key])
4.6.1.7 Tuplas y diccionarios - métodos

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

dictionary = {"gato" : "chat", "perro" : "chien", "caballo" :


"cheval"}

for spanish, french in dictionary.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:

gato -> chat


perro -> chien
caballo -> cheval
salida

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:

dictionary = {"gato" : "chat", "perro" : "chien", "caballo" :


"cheval"}

for french in dictionary.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:

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

for spanish, french in dictionary.items():


print(spanish, "->", french)

4.6.1.8 Tuplas y diccionarios

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

dictionary = {'gato': 'minou', 'perro': 'chien', 'caballo': 'cheval'}

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

La salida es:

{'gato': 'minou', 'dog': 'chien', 'caballo': 'cheval'}


salida

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 válido:

dictionary = {"gato" : "chat", "perro" : "chien", "caballo" :


"cheval"}

dictionary['cisne'] = 'cygne'
print(dictionary)
El ejemplo muestra como salida:
{'gato': 'chat', 'perro': 'chien', 'caballo': 'cheval', 'cisne':
'cygne'}
salida

EXTRA

También es posible insertar un elemento al diccionario utilizando el método update() ,


por ejemplo:

dictionary = {"gato" : "chat", "perro" : "chien", "caballo" :


"cheval"}

dictionary.update({"pato": "canard"})
print(dictionary)

Eliminado una clave


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

dictionary = {"gato" : "chat", "perro" : "chien", "caballo" :


"cheval"}

del dictionary['perro']
print(dictionary)

Nota: el eliminar una clave no existente, provocará un error.

El ejemplo da como salida:

{'gato': 'chat', 'caballo': 'cheval'}


salida

EXTRA

Para eliminar el ultimo elemento de la lista, se puede emplear el método popitem() :

dictionary = {"gato" : "chat", "perro" : "chien", "caballo" :


"cheval"}
dictionary.popitem()
print(dictionary) # salida: {'gato': 'chat', 'perro': 'chien'}

En versiones anteriores de Python, por ejemplo, antes de la 3.6.7, el método popitem()


elimina un elemento al azar del diccionario.

dictionary = {"gato" : "perro", "dog" : "chien", "caballo" :


"cheval"}

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

4.6.1.9 Tuplas y diccionarios trabajando juntos

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 un nombre vacío finaliza el ingreso de los datos (nota 1: ingresar una
puntuación vacía generará la excepción ValueError, pero no te preocupes por eso
ahora, verás cómo manejar tales casos cuando hablemos de excepciones en el
segundo parte de la serie del curso).
• Una lista con todos los nombre y el promedio de cada alumno debe ser mostrada
al final.

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

Ahora se analizará línea por línea:

• Línea 1: crea un diccionario vacío para ingresar los datos: el nombre del alumno
es empleado como clave, mientras que todas las calificaciones asociadas son
almacenadas en una tupla (la tupla puede ser el valor de un diccionario, esto no
es un problema).
• Línea 3: se ingresa a un bucle "infinito" (no te preocupes, saldrémos de el en el
momento indicado).
• Línea 4: se lee el nombre del alumno aquí.
• Línea 5-6: si el nombre es una cadena vacía (), salimos del bucle.
• Línea 8: se pide la calificación del estudiante (un valor entero en el rango del 1-10).
• Línea 9-10: si la puntuación ingresada no está dentro del rango de 0 a 10, se
abandona el bucle.
• Línea 12-13: 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 14-15: 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 17: se itera a través de los nombres ordenados de los estudiantes.
• Línea 18-19: inicializa los datos necesarios para calcular el promedio (sum y
counter).
• Línea 20-22: se itera a través de la tupla, tomado todas las calificaciones
subsecuentes y actualizando la suma junto con el contador.
• Línea 23: se calcula e imprime el promedio del alumno junto con su nombre.

Este es un ejemplo del programa:

Ingresa el nombre del estudiante: Bob


Ingresa la calificación del estudiante (0-10): 7
Ingresa el nombre del estudiante: Andy
Ingresa la calificación del estudiante (0-10): 3
Ingresa el nombre del estudiante: Bob
Ingresa la calificación del estudiante (0-10): 2
Ingresa el nombre del estudiante: Andy
Ingresa la calificación del estudiante (0-10): 10
Ingresa el nombre del estudiante: Andy
Ingresa la calificación del estudiante (0-10): 3
Ingresa el nombre del estudiante: Bob
Ingresa la calificación del estudiante (0-10): 9
Ingresa el nombre del estudiante:
Andy : 5.333333333333333
Bob : 6.0
Salida

school_class = {}

while True:
name = input("Ingresa el nombre del estudiante: ")
if name == '':
break

score = int(input("Ingresa la calificación del estudiante (0-10): "))


if score not in range(0, 11):
break

if name in school_class:
school_class[name] += (score,)
else:
school_class[name] = (score,)

for name in sorted(school_class.keys()):


adding = 0
counter = 0
for score in school_class[name]:
adding += score
counter += 1
print(name, ":", adding / counter)

4.6.1.10 RESUMEN DE SECCIÓN (1/3)

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:

my_tuple = (1, 2, True, "una cadena", (3, 4), [5, 6], None)
print(my_tuple)

my_list = [1, 2, True, "una cadena", (3, 4), [5, 6], None]
print(my_list)

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:

empty_tuple = ()
print(type(empty_tuple)) # salida: <class 'tuple'>

3. La tupla de un solo elemento se define de la siguiente manera:

one_elem_tuple_1 = ("uno", ) # Paréntesis y una coma.


one_elem_tuple_2 = "uno", # Sin paréntesis, solo la coma.

Si se elimina la coma, Python creará una variable no una tupla:


my_tuple_1 = 1,

print(type(my_tuple_1)) # salida: <class 'tuple'>

my_tuple_2 = 1 # Esto no es una tupla.


print(type(my_tuple_2)) # salida: <class 'int'>

4. Se pueden acceder los elementos de la tupla al indexarlos:

my_tuple = (1, 2.0, "cadena", [3, 4], (5, ), True)


print(my_tuple[3]) # salida: [3, 4]
5. Las tuplas son immutable, lo que significa que no se puede agregar, modificar, cambiar
o quitar elementos. El siguiente fragmento de código provocará una excepción:

my_tuple = (1, 2.0, "cadena", [3, 4], (5, ), True)


my_tuple[2] = "guitarra" # La excepción TypeError será lanzada.

Sin embargo, se puede eliminar la tupla completa:

my_tuple = 1, 2, 3,
del my_tuple
print(my_tuple) # NameError: name 'my_tuple' is not defined

6. Puedes iterar 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
tuple_1 = (1, 2, 3)
for elem in tuple_1:
print(elem)

# Ejemplo 2
tuple_2 = (1, 2, 3, 4)
print(5 in tuple_2)
print(5 not in tuple_2)

# Ejemplo 3
tuple_3 = (1, 2, 3, 5)
print(len(tuple_3))

# Ejemplo 4
tuple_4 = tuple_1 + tuple_2
tuple_5 = tuple_3 * 2

print(tuple_4)
print(tuple_5)

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:

my_tuple = tuple((1, 2, "cadena"))


print(my_tuple)

my_list = [2, 4, 6]
print(my_list) # salida: [2, 4, 6]
print(type(my_list)) # salida: <class 'list'>
tup = tuple(my_list)
print(tup) # salida: (2, 4, 6)
print(type(tup)) # salida: <class 'tuple'>

De la misma manera, cuando se desea convertir un iterable en una lista, se puede


emplear la función integrada de Python denominada list() :

tup = 1, 2, 3,
my_list = list(tup)
print(type(my_list)) # salida: <class 'list'>

4.6.1.11 RESUMEN DE SECCIÓN (2/3)

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:

my_dictionary = {
key1: value1,
key2: value2,
key3: value3,
}

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

pol_esp_dictionary = {
"kwiat": "flor",
"woda": "agua",
"gleba": "tierra"
}

item_1 = pol_esp_dictionary["gleba"] # Ejemplo 1.


print(item_1) # salida: tierra

item_2 = pol_esp_dictionary.get("woda") # Ejemplo 2.


print(item_2) # salida: agua

3. Si se desea cambiar el valor asociado a una clave específica, se puede hacer haciendo
referencia a la clave del elemento, a continuación se muestra un ejemplo:

pol_esp_dictionary = {
"zamek" : "castillo",
"woda" : "agua",
"gleba" : "tierra"
}

pol_esp_dictionary["zamek"] = "cerradura"
item = pol_esp_dictionary["zamek"]
print(item) # salida: cerradura

4. Para agregar o eliminar una clave (junto con su valor asociado), emplea la siguiente
sintaxis:

phonebook = {} # un diccionario vacío

phonebook["Adán"] = 3456783958 # crear/agregar un par clave-valor


print(phonebook) # salida: {'Adán': 3456783958}

del phonebook["Adán"]
print(phonebook) # 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:

pol_esp_dictionary = {"kwiat": "flor"}

pol_esp_dictionary.update({"gleba": "tierra"})
print(pol_esp_dictionary) # salida: {'kwiat': 'flor', 'gleba': 'tierra'}

pol_esp_dictionary.popitem()
print(pol_esp_dictionary) # salida: {'kwiat': 'flor'}

5. Se puede emplear el bucle for para iterar a través del diccionario, por ejemplo:

pol_esp_dictionary = {
"zamek": "castillo",
"woda": "agua",
"gleba": "tierra"
}

for item in pol_esp_dictionary:


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:

pol_esp_dictionary = {
"zamek" : "castillo",
"woda" : "agua",
"gleba" : "tierra"
}

for key, value in pol_esp_dictionary.items():


print("Pol/Esp ->", key, ":", value)

7. Para comprobar si una clave existe en un diccionario, se puede emplear la palabra


clave reservada in :

pol_esp_dictionary = {
"zamek" : "castillo",
"woda" : "agua",
"gleba" : "tierra"
}

if "zamek" in pol_esp_dictionary:
print("Si")
else:
print("No")

8. Se puede emplear la palabra reservada del para eliminar un elemento, o un


diccionario entero. Para eliminar todos los elementos de un diccionario se debe emplear
el método clear() :

pol_esp_dictionary = {
"zamek" : "castillo",
"woda" : "agua",
"gleba" : "tierra"
}

print(len(pol_esp_dictionary)) # salida: 3
del pol_esp_dictionary["zamek"] # eliminar un elemento
print(len(pol_esp_dictionary)) # salida: 2

pol_esp_dictionary.clear() # eliminar todos los elementos


print(len(pol_esp_dictionary)) # salida: 0

del pol_esp_dictionary # elimina el diccionario

9. Para copiar un diccionario, emplea el método copy() :

pol_esp_dictionary = {
"zamek" : "castillo",
"woda" : "agua",
"gleba" : "tierra"
}

copy_dictionary = pol_esp_dictionary.copy()
4.6.1.12 RESUMEN DE SECCIÓN (3/3)
Puntos Claves: Tuplas y diccionarios
Ejercicio 1
¿Qué ocurrirá cuando se intente ejecutar el siguiente código?
my_tup = (1, 2, 3)
print(my_tup[2])

Revisar

El programa imprimirá 3 en pantalla.

Ejercicio 2
¿Cuál es la salida del siguiente fragmento de código?
tup = 1, 2, 3
a, b, c = tup

print(a * b * c)

Revisar

El programa imprimirá 6 en pantalla. Los elementos de la tupla tup han sido


"desempaquetados" en las variables a , b , y c .

Ejercicio 3
Completa el código para emplear correctamente el método count() para encontrar la
cantidad de 2 duplicados en la tupla siguiente.
tup = 1, 2, 3, 2, 4, 5, 6, 2, 7, 2, 8, 9
duplicates = # Escribe tu código aquí.

print(duplicates) # salida: 4

Revisar

tup = 1, 2, 3, 2, 4, 5, 6, 2, 7, 2, 8, 9
duplicates = tup.count(2)

print(duplicates) # salida: 4

Ejercicio 4
Escribe un programa que "una" los dos diccionarios ( d1 y d2 ) para crear uno nuevo ( d3 ).
d1 = {'Adam Smith': 'A', 'Judy Paxton': 'B+'}
d2 = {'Mary Louis': 'A', 'Patrick White': 'C'}
d3 = {}

for item in (d1, d2):


# Escribe tu código aquí.

print(d3)
Revisar

Solución muestra:

d1 = {'Adam Smith': 'A', 'Judy Paxton': 'B+'}


d2 = {'Mary Louis': 'A', 'Patrick White': 'C'}
d3 = {}

for item in (d1, d2):


d3.update(item)

print(d3)

Ejercicio 5
Escribe un programa que convierta la lista my_list en una tupla.

my_list = ["car", "Ford", "flower", "Tulip"]

t = # Escribe tu código aquí.


print(t)

Revisar

Solución muestra:
my_list = ["car", "Ford", "flower", "Tulip"]

t = tuple(my_list)
print(t)

Ejercicio 6
Escribe un programa que convierta la tupla colors en un diccionario.

colors = (("green", "#008000"), ("blue", "#0000FF"))

# Escribe tu código aquí.

print(colors_dictionary)

Revisar

Solución muestra:

colors = (("green", "#008000"), ("blue", "#0000FF"))

colors_dictionary = dict(colors)
print(colors_dictionary)

Ejercicio 7
¿Que ocurrirá cuando se ejecute el siguiente código?
my_dictionary = {"A": 1, "B": 2}
copy_my_dictionary = my_dictionary.copy()
my_dictionary.clear()

print(copy_my_dictionary)

Revisar

El programa mostrará {'A': 1, 'B': 2} en pantalla.

Ejercicio 8
¿Cuál es la salida del siguiente programa?
colors = {
"blanco": (255, 255, 255),
"gris": (128, 128, 128),
"rojo": (255, 0, 0),
"verde": (0, 128, 0)
}

for col, rgb in colors.items():


print(col, ":", rgb)

Revisar

white : (255, 255, 255)


grey : (128, 128, 128)
red : (255, 0, 0)
green : (0, 128, 0)

4.7.1.1 Excepciones

Errores: el pan diario del desarrollador


Parece indiscutible que todos los programadores (incluso tú) quieren escribir código libre
de errores y hacen todo lo posible para lograr este objetivo. Desafortunadamente, nada
es perfecto en este mundo y el software no es una excepción. Presta atención a la
palabra "excepción", ya que la veremos de nuevo muy pronto en un significado que no
tiene nada que ver con el común.
Errar es humano, es imposible no cometer errores y es imposible escribir código sin
errores. No nos malinterpretes, no queremos convencerte de que escribir programas
desordenados y defectuosos es una virtud. Más bien queremos explicar que incluso el
programador más cuidadoso no puede evitar defectos menores o mayores. Sólo aquellos
que no hacen nada no cometen errores.

Paradójicamente, aceptar esta difícil verdad puede convertirte en un mejor programador


y mejorar la calidad de tu código.

Te preguntarás: "¿Cómo podría ser esto posible?".

Intentaremos mostrártelo.

Errores en los datos frente a errores en el código


El lidiar con errores de programación tiene (al menos) dos partes. La primera es cuando
te metes en problemas porque tu código, aparentemente correcto, se alimenta con datos
incorrectos. Por ejemplo, esperas que se ingrese al código un valor entero, pero tu
usuario descuidado ingresa algunas letras al azar.

Puede suceder que tu código termine en ese momento y el usuario se quede solo con un
mensaje de error conciso y a la vez ambiguo en la pantalla. El usuario estará insatisfecho
y tu también deberías estarlo. Te mostraremos cómo proteger tu código de este tipo de
fallas y cómo no provocar la ira del usuario.

La segunda parte de lidiar con errores de programación se revela cuando ocurre un


comportamiento no deseado del programa debido a errores que se cometieron cuando
se estaba escribiendo el código. Este tipo de error se denomina comúnmente "bug"
(bicho en inglés), que es una manifestación de una creencia bien establecida de que, si un
programa funciona mal, esto debe ser causado por bichos maliciosos que viven dentro
del hardware de la computadora y causan cortocircuitos u otras interferencias.

Esta idea no es tan descabellada como puede parecer: incidentes de este tipo eran
comunes en tiempos en que las computadoras ocupaban grandes pasillos, consumían
kilovatios de electricidad y producían enormes cantidades de calor. Afortunadamente, o
no, estos tiempos se han ido para siempre y los únicos errores que pueden estropear tu
código son los que tú mismo sembraste en el código. Por lo tanto, intentaremos
mostrarte cómo encontrar y eliminar tus errores, en otras palabras, cómo depurar tu
código.

Comencemos el viaje por la tierra de los errores y las fallas.


4.7.1.2 Excepciones

Cuando los datos no son lo que deberían ser


Escribamos un fragmento de código extremadamente trivial: leerá un número natural (un
entero no negativo) e imprimirá su recíproco. De esta forma, 2 se convertirá en 0.5 (1/2)
y 4 en 0.25 (1/4). Aquí está el programa

¿Hay algo que pueda salir mal? El código es tan breve y compacto que no parece que
vayamos a encontrar ningún problema allí.

Parece que ya sabes hacia dónde vamos. Sí, tienes razón: ingresar datos que no sean un
número entero (que también incluye ingresar nada) arruinará completamente la
ejecución del programa. Esto es lo que verá el usuario del código:

Traceback (most recent call last):


File "code.py", line 1, in
value = int(input('Ingresa un número natural: '))
ValueError: invalid literal for int() with base 10: ''
output

Todas las líneas que muestra Python son significativas e importantes, pero la última línea
parece ser la más valiosa. La primera palabra de la línea es el nombre de la excepción la
cual provoca que tu código se detenga. Su nombre aquí es ValueError . El resto de la
línea es solo una breve explicación que especifica con mayor precisión la causa de la
excepción ocurrida.

¿Cómo lo afrontas? ¿Cómo proteges tu código de la terminación abrupta, al usuario de la


decepción y a ti mismo de la insatisfacción del usuario?

La primera idea que se te puede ocurrir es verificar si los datos proporcionados por el
usuario son válidos y negarte a cooperar si los datos son incorrectos. En este caso, la
verificación puede basarse en el hecho de que esperamos que la cadena de entrada
contenga solo dígitos.

Ya deberías poder implementar esta verificación y escribirla tu mismo, ¿no es así?


También es posible comprobar si la variable value es de tipo int (Python tiene un medio
especial para este tipo de comprobaciones: es un operador llamado is . La revisión en sí
puede verse de la siguiente manera:

type(value) is int

Su resultado es verdadero si el valor actual de la variable value es del tipo int .

Perdónanos si no dedicamos más tiempo a esto ahora; encontrarás explicaciones más


detalladas sobre el operador is en un módulo del curso dedicado a la programación
orientada a objetos.
Es posible que te sorprendas al saber que no queremos que realices ninguna validación
preliminar de datos. ¿Por qué? Porque esta no es la forma que Python recomienda.

value = int(input('Ingresa un número natural: '))


print('El recíproco de', value, 'es', 1/value)

4.7.1.3 Excepciones

El Código Python
En el mundo de Python, hay una regla que dice: "Es mejor pedir perdón que pedir
permiso".

Detengámonos aquí por un momento. No nos malinterpretes, no queremos que apliques


la regla en tu vida diaria. No tomes el automóvil de nadie sin permiso, con la esperanza
de que puedas ser tan convincente que evites la condena por lo ocurrido. La regla se
trata de otra cosa.

En realidad, la regla dice: "es mejor manejar un error cuando ocurre que tratar de
evitarlo".

"De acuerdo", puedes decir, "pero ¿cómo debo pedir perdón cuando el programa finaliza
y no queda nada que más por hacer?". Aquí es donde algo llamado excepción entra en
escena.

Observa el código en el editor.

Puedes ver dos bloques aquí:

• El primero, comienza con la palabra clave reservada try: este es el lugar donde
se coloca el código que se sospecha que es riesgoso y puede terminar en caso de
un error; nota: este tipo de error lleva por nombre excepción, mientras que la
ocurrencia de la excepción se le denomina generar; podemos decir que se genera
(o se generó) una excepción.
• • El segundo, la parte del código que comienza con la palabra clave
reservada except: esta parte fue diseñada para manejar la excepción; depende
de ti lo que quieras hacer aquí: puedes limpiar el desorden o simplemente puede
barrer el problema debajo de la alfombra (aunque se prefiere la primera
solución).

Entonces, podríamos decir que estos dos bloques funcionan así:

• La palabra clave reservada try marca el lugar donde intentas hacer algo sin
permiso.
• La palabra clave reservada except comienza un lugar donde puedes mostrar tu
talento para disculparte o pedir perdón.
Como puedes ver, este enfoque acepta errores (los trata como una parte normal de la
vida del programa) en lugar de intensificar los esfuerzos para evitarlos por completo.

try:
# Es un lugar donde
# tu puedes hacer algo
# sin pedir permiso.
except:
# Es un espacio dedicado
# exclusivamente para pedir perdón.

4.7.1.4 Excepciones

La excepción confirma la regla


Reescribamos el código para adoptar el enfoque de Python para la vida.

Resumamos lo que hemos hablado:

• Cualquier fragmento de código colocado entre try y except se ejecuta de una


manera muy especial: cualquier error que ocurra aquí dentro no terminará la
ejecución del programa. En cambio, el control saltará inmediatamente a la
primera línea situada después de la palabra clave reservada except , y no se
ejecutará ninguna otra línea del bloque try .
• El código en el bloque except se activa solo cuando se ha encontrado una
excepción dentro del bloque try . No hay forma de llegar por ningún otro medio.
• Cuando el bloque try o except se ejecutan con éxito, el control vuelve al proceso
normal de ejecución y cualquier código ubicado más allá en el archivo fuente se
ejecuta como si no hubiera pasado nada.

Ahora queremos hacerte una pregunta: ¿Es ValueError la única forma en que el control
podría caer dentro del bloque except ?

¡Analiza el código cuidadosamente y piensa en tu respuesta!

try:
value = input('Ingresa un número natural: ')
print('El recíproco de', value, 'es', 1/int(value))
except:
print('No se que hacer con', value)
4.7.1.5 Excepciones

Cómo lidiar con más de una excepción


La respuesta obvia es "no": hay más de una forma posible de plantear una excepción. Por
ejemplo, un usuario puede ingresar cero como entrada, ¿puedes predecir lo que
sucederá a continuación?

Sí, tienes razón: la división colocada dentro de la invocación de la


función print() generará la excepción ZeroDivisionError . Como es de esperarse, el
comportamiento del código será el mismo que en el caso anterior: el usuario verá el
mensaje "No se que hacer con...", lo que parece bastante razonable en este contexto,
pero también es posible que desees manejar este tipo de problema de una manera un
poco diferente.

¿Es posible? Por supuesto que lo es. Hay al menos dos enfoques que puedes
implementar aquí.

El primero de ellos es simple y complicado al mismo tiempo: puedes agregar dos


bloques try por separado, uno que incluya la invocación de la función input() donde se
puede generar la excepción ValueError , y el segundo dedicado a manejar posibles
problemas inducidos por la división. Ambos bloques try tendrían su propio except , y de
esa manera, tendrías un control total sobre dos errores diferentes.

Esta solución es buena, pero es un poco larga: el código se hincha innecesariamente.


Además, no es el único peligro que te espera. Toma en cuenta que dejar el primer
bloque try-except deja mucha incertidumbre; tendrás que agregar código adicional
para asegurarte de que el valor que ingresó el usuario sea seguro para usar en la división.
Así es como una solución aparentemente simple se vuelve demasiado complicada.

Afortunadamente, Python ofrece una forma más sencilla de afrontar este tipo de
desafíos.

Dos excepciones después de un try.


Observa el código.

Como puedes ver, acabamos de agregar un segundo except . Esta no es la única


diferencia; toma en cuenta que ambos except tienen nombres de excepción específicos.
En esta variante, cada una de las excepciones esperadas tiene su propia forma de
manejar el error, pero se debe enfatizarse en que solo una de todas puede interceptar el
control; si se ejecuta una, todas las demás permanecen inactivas. Además, la cantidad de
excepciones no está limitado: puedes especificar tantas o tan pocas como necesites, pero
no se te olvide que ninguna de las excepciones se puede especificar más de una vez.

Pero esta todavía no es la última palabra de Python sobre excepciones.


try:
value = input('Ingresa un número natural: ')
print('El recíproco de', value, 'es', 1/int(value))
except ValueError:
print('No se que hacer con', value)
except ZeroDivisionError:
print('La división entre cero no está permitida en nuestro Universo.')

4.7.1.6 Excepciones

La excepción por defecto y cómo usarla


El código ha cambiado de nuevo, ¿puedes ver la diferencia?

Hemos agregado un tercer except , pero esta vez no tiene un nombre de


excepción específico; podemos decir que es anónimo o (lo que está más cerca de su
función real) es el por defecto. Puedes esperar que cuando se genere una excepción y no
haya un except dedicado a esa excepción, esta será manejada por la excepción por
defecto.

Nota:

¡el except por defecto debe ser el último except ! ¡Siempre!

try:
value = input('Ingresa un número natural: ')
print('El recíproco de', value, 'es', 1/int(value))
except ValueError:
print('No se que hacer con', value)
except ZeroDivisionError:
print('La división entre cero no está permitida en nuestro Universo.')
except:
print('Ha sucedido algo extraño, ¡lo siento!')

4.7.1.7 Excepciones

Algunas excepciones útiles


Analicemos con más detalle algunas excepciones útiles (o más bien, las más comunes)
que puedes llegar a experimentar.

ZeroDivisionError
Esta aparece cuando intentas forzar a Python a realizar cualquier operación que
provoque una división en la que el divisor es cero o no se puede distinguir de cero. Toma
en cuenta que hay más de un operador de Python que puede hacer que se genere esta
excepción. ¿Puedes adivinarlos todos?
Si, estos son: /, //, y %.

ValueError
Espera esta excepción cuando estás manejando valores que pueden usarse de manera
inapropiada en algún contexto. En general, esta excepción se genera cuando una función
(como int() o float() ) recibe un argumento de un tipo adecuado, pero su valor es
inaceptable.

TypeError
Esta excepción aparece cuando intentas aplicar un dato cuyo tipo no se puede aceptar en
el contexto actual. Mira el ejemplo:

short_list = [1]
one_value = short_list[0.5]

No está permitido usar un valor flotante como índice de una lista (la misma regla también
se aplica a las tuplas). TypeError es un nombre adecuado para describir el problema y
una excepción adecuada a generar.

AttributeError
Esta excepción llega, entre otras ocasiones, cuando intentas activar un método que no
existe en un elemento con el que se está tratando. Por ejemplo:

short_list = [1]
short_list.append(2)
short_list.depend(3)

La tercera línea de nuestro ejemplo intenta hacer uso de un método que no está incluido
en las listas. Este es el lugar donde se genera la excepción AttributeError .

SyntaxError
Esta excepción se genera cuando el control llega a una línea de código que viola la
gramática de Python. Puede sonar extraño, pero algunos errores de este tipo no se
pueden identificar sin ejecutar primero el código. Este tipo de comportamiento es típico
de los lenguajes interpretados: el intérprete siempre trabaja con prisa y no tiene tiempo
para escanear todo el código fuente. Se conforma con comprobar el código que se está
ejecutando en el momento. Muy pronto se te presentará un ejemplo de esta categoría.

Es una mala idea manejar este tipo de excepciones en tus programas. Deberías producir
código sin errores de sintaxis, en lugar de enmascarar las fallas que has causado.
4.7.1.8 Excepciones

Por qué no se puede evitar el probar tu código


Aunque vamos a resumir nuestras consideraciones excepcionales aquí, no creas que es
todo lo que Python puede ofrecer para ayudarte a suplicar perdón. La maquinaria de
excepciones de Python es mucho más compleja y sus capacidades te permiten
desarrollar estrategias de manejo de errores expandidas. Volveremos a estos temas, lo
prometemos. No dudes en realizar tus experimentos y sumergirte en las excepciones por
ti mismo.

Ahora queremos contarte sobre el segundo lado de la lucha interminable contra los
errores: el destino inevitable de la vida de un desarrollador. Como no puedes evitar la
creación de errores en tu código, siempre debes estar listo para buscarlos y destruirlos.
No entierres la cabeza en la arena: ignorar los errores no los hará desaparecer.

Un deber importante para los desarrolladores es probar el código recién creado, pero no
debes olvidar que las pruebas no son una forma de demostrar que el código está libre de
errores. Paradójicamente, lo único que las pruebas determinan, es que tu código
contiene errores. No creas que puedes relajarte después de una prueba exitosa.

El segundo aspecto importante de las pruebas de software es estrictamente psicológico.


Es una verdad conocida desde hace años que los autores, incluso aquellos que son
confiables y conscientes de sí mismos, no pueden evaluar y verificar objetivamente sus
trabajos.

Es por eso por lo que cada novelista necesita un editor y cada programador necesita un
"tester". Algunos dicen, con un poco de rencor, pero con sinceridad, que los
desarrolladores prueban su código para mostrar su perfección, no para encontrar
problemas que puedan frustrarlos. Los "testers" o probadores están libres de tales
dilemas, y es por eso por lo que su trabajo es más efectivo y rentable.

Por supuesto, esto no te exime de estar atento y cauteloso. Prueba tu código lo mejor
que puedas. No facilites demasiado el trabajo a los probadores.

Su deber principal es asegurarse de haber verificado todas las rutas o caminos de


ejecución por las que puede pasar tu código. ¿Suena misterioso? ¡Por supuesto que no!

Rastreando las rutas de ejecución


Supón que acabas de terminar de escribir este fragmento de código.

Existen tres rutas o caminos de ejecución independientes en el código, ¿puedes verlas?


Están determinadas por las sentencias if-elif-else. Por supuesto, las rutas de
ejecución pueden construirse mediante muchas otras sentencias como bucles, o incluso
bloques try-except.
Si vas a probar tu código de manera justa y quieres dormir profundamente y soñar sin
pesadillas (las pesadillas sobre errores pueden ser devastadoras para el rendimiento de
un desarrollador), estás obligado a preparar un conjunto de datos de prueba que
obligará a tu código a negociar todos los posibles caminos.

En nuestro ejemplo, el conjunto debe contener al menos tres valores flotantes: uno
positivo, uno negativo y cero.

temperature = float(input('Ingresa la temperatura actual:'))

if temperature > 0:
print("Por encima de cero")
elif temperature < 0:
print("Por debajo de cero")
else:
print("Cero")

4.7.1.9 Excepciones

Cuando Python cierra sus ojos


Tal prueba es crucial. Queremos mostrarte por qué no debes omitirlo. Observa el
siguiente código.

Introdujimos intencionalmente un error en el código; esperamos que tus ojos atentos lo


noten de inmediato. Sí, eliminamos solo una letra y, en efecto, la invocación válida de la
función print() se convierte en la obviamente inválida invocación " prin() ". No existe
tal función como " prin() " en el alcance de nuestro programa, pero ¿es realmente obvio
para Python?

Ejecuta el código e ingresa un 0 .

Como puedes ver, el código finaliza su ejecución sin ningún obstáculo.

¿Cómo es eso posible? ¿Por qué Python pasa por alto un error de desarrollador tan
evidente?

¿Puedes encontrar las respuestas a estas preguntas fundamentales?

temperature = float(input('Ingresa la temperatura actual:'))

if temperature > 0:
print("Por encima de cero")
elif temperature < 0:
prin("Por debajo de cero")
else:
print("Cero")
4.7.1.10 Excepciones

Pruebas y probadores
La respuesta es más simple de lo esperado y también un poco decepcionante. Python,
como seguramente sabes, es un lenguaje interpretado. Esto significa que el código fuente
se analiza y ejecuta al mismo tiempo. En consecuencia, es posible que Python no tenga
tiempo para analizar las líneas de código que no están sujetas a ejecución. Como dice un
antiguo dicho de los desarrolladores: "es una característica, no un error" (no utilices esta
frase para justificar el comportamiento extraño de tu código).

¿Entiendes ahora por qué el pasar por todos los caminos de ejecución es tan vital e
inevitable?

Supongamos que terminas tu código y que las pruebas que has realizado son exitosas.
Entregas tu código a los probadores y, ¡afortunadamente! - encontraron algunos errores
en él. Estamos usando la palabra "afortunadamente" de manera completamente
consciente. Debes aceptar que, en primer lugar, los probadores son los mejores amigos
del desarrollador; no debes tratar a los errores que ellos encuentran como una ofensa o
una malignidad; y, en segundo lugar, cada error que encuentran los probadores es un
error que no afectará a los usuarios. Ambos factores son valiosos y merecen tu atención.

Ya sabes que tu código contiene un error o errores (lo segundo es más probable). Ahora,
¿cómo los localizas y cómo arreglas tu código?

Error frente a depuración (Bug vs. debug)


La medida básica que un desarrollador puede utilizar contra los errores es, como era de
esperarse, un depurador, mientras que el proceso durante el cual se eliminan los errores
del código se llama depuración. Según un viejo chiste, la depuración es un complicado
juego de misterio en el que eres simultáneamente el asesino, el detective y, la parte más
dolorosa de la intriga, la víctima. ¿Estás listo para interpretar todos estos roles? Entonces
debes armarte con un depurador.

Un depurador es un software especializado que puede controlar cómo se ejecuta tu


programa. Con el depurador, puedes ejecutar tu código línea por línea, inspeccionar
todos los estados de las variables y cambiar sus valores en cualquier momento sin
modificar el código fuente, detener la ejecución del programa cuando se cumplen o no
ciertas condiciones, y hacer muchas otras tareas útiles.
Podemos decir que todo IDE está equipado con un depurador más o menos avanzado.
Incluso IDLE tiene uno, aunque puedes encontrar su manejo un poco complicado y
problemático. Si deseas utilizar el depurador integrado de IDLE, debes activarlo mediante
la opción "Debug" en la barra de menú de la ventana principal de IDLE. Es el punto de
partida para la depuración.

Las capturas de pantalla que ves al lado muestran el depurador IDLE durante una simple
sesión de depuración.

Puedes ver cómo el depurador visualiza las variables y los valores de los parámetros.
Observa la pila de llamadas que muestra la cadena de invocaciones que van desde la
función actualmente ejecutada hacia el intérprete.

Si deseas saber más sobre el depurador IDLE, consulta la documentación IDLE.

4.7.1.11 Excepciones

Depuración por impresión


Esta forma de depuración, que se puede aplicar a tu código mediante cualquier tipo de
depurador, a veces se denomina depuración interactiva. El significado del término se
explica por sí mismo: el proceso necesita su interacción (la del desarrollador) para que se
lleve a cabo.

Algunas otras técnicas de depuración se pueden utilizar para cazar errores. Es posible
que no puedas o no quieras usar un depurador (las razones pueden variar). ¿Estás
entonces indefenso? ¡Absolutamente no!

Puedes utilizar una de las tácticas de depuración más simples y antiguas (pero aún útil)
conocida como la depuración por impresión. El nombre habla por sí mismo: simplemente
insertas varias invocaciones print() adicionales dentro de tu código para generar datos
que ilustran la ruta que tu código está negociando actualmente. Puedes imprimir los
valores de las variables que pueden afectar la ejecución.

Estas impresiones pueden generar texto significativo como "Estoy aquí", "Ingresé a la
función foo() ", "El resultado es 0 ", o pueden contener secuencias de caracteres que solo
tu puedes leer. Por favor, no uses palabras obscenas o indecentes para ese propósito,
aunque puedas sentir una fuerte tentación; tu reputación puede arruinarse en un
momento si estas payasadas se filtran al público.

Como puedes ver, este tipo de depuración no es realmente interactiva en lo absoluto, o


es interactiva solo en pequeña medida, cuando decides aplicar la función input() para
detener o retrasar la ejecución del código.
Una vez que se encuentran y eliminan los errores, las impresiones adicionales pueden
comentarse o eliminarse; tu decides. No permitas que se ejecuten en el código final;
pueden confundir tanto a los probadores como a los usuarios, y traer mal karma sobre ti.

4.7.1.12 Excepciones

Algunos consejos útiles


Aquí hay algunos consejos que pueden ayudarte a encontrar y eliminar errores. Ninguno
de ellos es definitivo. Úsalos de manera flexible y confía en tu intuición. No te creas a ti
mismo, comprueba todo dos veces.

1. Intenta decirle a alguien (por ejemplo, a tu amigo o compañero de trabajo) qué es


lo que se espera que haga tu código y cómo se espera que se comporte. Se
concreto y no omitas detalles. Responde todas las preguntas que te hagan. Es
probable que te des cuenta de la causa del problema mientras cuentas tu historia,
ya que el hablar activa esas partes de tu cerebro que permanecen inactivas
mientras codificas. Si ningún humano puede ayudarte con el problema, usa un
patito amarillo de goma en su lugar. No estamos bromeando, consulta el artículo
de Wikipedia para obtener más información sobre esta técnica de uso
común: Método de depuración del patito de goma.

2. Intenta aislar el problema. Puedes extraer la parte de tu código que se sospecha


que es responsable de tus problemas y ejecutarla por separado. Puedes comentar
partes del código para ocultar el problema. Asigna valores concretos a las
variables en lugar de leerlos desde la consola. Prueba tus funciones aplicando
valores de argumentos predecibles. Analiza el código cuidadosamente. Léelo en
voz alta.

3. Si el error apareció recientemente y no había aparecido antes, analiza todos los


cambios que has introducido en tu código; uno de ellos puede ser la razón.
4. Tómate un descanso, bebe una taza de café, toma a tu perro y sal a caminar, lee
un buen libro, incluso dos, haz una llamada telefónica a tu mejor amigo; te
sorprenderás de la frecuencia con la que esto ayuda.

5. Se optimista: eventualmente encontrarás el error; te lo prometemos.

Prueba unitaria: un nivel más alto de codificación


También existe una técnica de programación importante y ampliamente utilizada que
tendrás que adoptar tarde o temprano durante tu carrera de desarrollador: se
llama prueba unitaria. El nombre puede ser un poco confuso, ya que no se trata solo de
probar el software, sino también (y, sobre todo) de cómo se escribe el código.

Para resumir la historia, las pruebas unitarias asumen que las pruebas son partes
inseparables del código y la preparación de los datos de prueba es una parte inseparable
de la codificación. Esto significa que cuando escribes una función o un conjunto de
funciones cooperativas, también estás obligado a crear un conjunto de datos para los
cuales el comportamiento de tu código es predecible y conocido.

Además, debes equipar a tu código con una interfaz que pueda ser utilizada por un
entorno de pruebas automatizado. En este enfoque, cualquier enmienda realizada al
código (incluso la menos significativa) debe ir seguida de la ejecución de todas las
pruebas unitarias que acompañan al código fuente.

Para estandarizar este enfoque y facilitar su aplicación, Python proporciona un módulo


dedicado llamado unittest . No vamos a discutirlo aquí, es un tema amplio y complejo.
Por lo tanto, hemos preparado un curso y una ruta de certificación independiente para
este tema. Se llama "Python para pruebas", y ahora te invitamos a participar en él.

¡Nos vemos pronto!

4.7.1.13 RESUMEN DE SECCIÓN

Puntos Clave: Excepciones


1. En Python, existe una distinción entre dos tipos de errores:

• Errores de sintaxis (errores de análisis), que ocurren cuando el analizador


encuentra una sentencia de código que no es correcta. Por ejemplo:

El intentar ejecutar la siguiente línea:

print("Hola, ¡Mundo!)
Provocará un error del tipo SyntaxError, y da como resultado el siguiente (o
similar) mensaje que se muestra en la consola:

File "main.py", line 1

print("Hola, ¡Mundo!)
^
SyntaxError: EOL while scanning string literal
salida

Presta atención a la flecha: indica el lugar donde el analizador de Python ha tenido


problemas. En este caso, la doble comilla es la que falta. ¿Lo notaste?

• Excepciones, ocurren incluso cuando una sentencia o expresión es


sintácticamente correcta. Estos son los errores que se detectan durante la
ejecución, cuando tu código da como resultado un error que no
es incondicionalmente fatal. Por ejemplo:

El intentar ejecutar la siguiente línea:

print(1/0)

Provocará una excepción ZeroDivisionError, y da como resultado el siguiente (o


similar) mensaje que se muestra en la consola:

Traceback (most recent call last):

File "main.py", line 1, in

print(1/0)

ZeroDivisionError: division by zero

salida

Presta atención a la última línea del mensaje de error; en realidad, te dice lo que
sucedió. Existen muchos diferentes tipos de excepciones,
como ZeroDivisionError, NameError, TypeError, y muchas mas; y esta parte del
mensaje te informa qué tipo de excepción se ha generado. Las líneas anteriores
muestran el contexto en el que ha ocurrido la excepción.

2. Puedes "capturar" y manejar excepciones en Python usando el bloque try-except. Por


lo tanto, si tienes la sospecha de que cualquier fragmento de código en particular puede
generar una excepción, puedes escribir el código que la manejará con elegancia y no
interrumpirá el programa. Observa el ejemplo:

while True:
try:
number = int(input("Ingresa un número entero: "))
print(number/2)
break
except:
print("Advertencia: el valor ingresado no es un número válido. Intenta de nuevo...")

El código anterior le pide al usuario que ingrese un valor hasta que el valor ingresado sea
un número entero válido. Si el usuario ingresa un valor que no se puede convertir a un
int, el programa imprimirá en la consola Advertencia: el valor ingresado no es un número
válido. Intenta de nuevo... , y pide al usuario que ingrese un número nuevamente. Veamos
que sucede en dicho caso.

1. El programa entra en el bucle while.


2. El bloque try se ejecuta y el usuario ingresa un valor incorrecto, por
ejemplo: ¡hola! .
3. Se genera una excepción y el resto del código del bloque try se omite. El
programa salta al bloque except , lo ejecuta, y luego se sigue al código que se
encuentra después del bloque try-except.

Si el usuario ingresa un valor correcto y no se genera ninguna excepción, las


instrucciones subsiguientes al bloque try, son ejecutadas. En este caso, los excepts
no se ejecutan.

3. Puedes manejar múltiples excepciones en tu bloque de código. Analiza los siguientes


ejemplos:

while True:
try:
number = int(input("Ingresa un número entero: "))
print(5/number)
break
except ValueError:
print("Valor incorrecto.")
except ZeroDivisionError:
print("Lo siento. No puedo dividir entre cero.")
except:
print("No se que hacer...")

Puedes utilizar varios bloques except dentro de una sentencia try, y especificar nombres
de excepciones. Si se ejecuta alguno de los except , los otros se omitirán. Recuerda:
puedes especificar una excepción integrada solo una vez. Además, no olvides que la
excepción por defecto (o genérica), es decir, a la que no se le especifica nombre, debe ser
colocada al final (utiliza las excepciones más específicas primero, y las más generales al
último).

También puedes especificar y manejar múltiples excepciones integradas dentro de un


solo bloque except:
while True:
try:
number = int(input("Ingresa un número entero: "))
print(5/number)
break
except (ValueError, ZeroDivisionError):
print("Valor incorrecto o se ha roto la regla de división entre cero.")
except:
print("Lo siento, algo salió mal...")

4. Algunas de las excepciones integradas más útiles de Python


son: ZeroDivisionError, ValueError, TypeError, AttributeError, y SyntaxError. Una
excepción más que, en nuestra opinión, merece tu atención es la
excepción KeyboardInterrupt, que se genera cuando el usuario presiona la tecla de
interrupción (CTRL-C o Eliminar). Ejecuta el código anterior y presiona la combinación de
teclas para ver qué sucede.

Para obtener más información sobre las excepciones integradas de Python, consulta la
documentación oficial de Python aquí.

5. Por último, pero no menos importante, debes recordar cómo probar y depurar tu
código. Utiliza técnicas de depuración como depuración de impresión; si es posible, pide
a alguien que lea tu código y te ayude a encontrar errores o mejorarlo; intenta aislar el
fragmento de código que es problemático y susceptible a errores, prueba tus
funciones aplicando valores de argumento predecibles, y trata de manejar las situaciones
en las que alguien ingresa valores incorrectos; comenta las partes del código que ocultan
el problema. Finalmente, toma descansos y vuelve a tu código después de un tiempo con
un par de ojos nuevos.

Ejercicio

¿Cuál es la salida del siguiente programa si el usuario ingresa un 0 ?

try:
value = int(input("Ingresa un número entero: "))
print(value/value)
except ValueError:
print("Entrada incorrecta...")
except ZeroDivisionError:
print("Entrada errónea...")
except:
print("¡Buuuu!")

Revisar

El programa dará como salida: Entrada errónea... .


4.7.2.1 PROYECTO: TIC-TAC-TOE

PROYECTO

Tiempo Estimado
30-60 minutos

Nivel de Dificultad
Medio/Difícil

Objetivos
• Perfeccionar las habilidades del estudiante al emplear Python para resolver
problemas complejos.
• La integración de técnicas de programación en un solo programa consistente de
varias partes.

Escenario
Tu tarea es escribir un simple programa que simule jugar a tic-tac-toe (nombre en inglés)
con el usuario. Para hacerlo más fácil, hemos decidido simplificar el juego. Aquí están
nuestras reglas:

• La maquina (por ejemplo, el programa) jugará utilizando las 'X' s.


• El usuario (por ejemplo, tu) jugarás utilizando las 'O' s.
• El primer movimiento es de la maquina: siempre coloca una 'X' en el centro del
tablero.
• Todos los cuadros están numerados comenzando con el 1 (observa el ejemplo
para que tengas una referencia).
• El usuario ingresa su movimiento introduciendo el 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 continua, el juego termina en empate, tu ganas, o la maquina gana.
• La maquina responde con su movimiento y se verifica el estado del juego.
• No se debe implementar algún tipo de inteligencia artificial, la maquina elegirá un
cuadro de manera aleatoria, eso es suficiente para este juego.

El ejemplo del programa es el siguiente:


+-------+-------+-------+
| | | |
| 1 | 2 | 3 |
| | | |
+-------+-------+-------+
| | | |
| 4 | X | 6 |
| | | |
+-------+-------+-------+
| | | |
| 7 | 8 | 9 |
| | | |
+-------+-------+-------+
Ingresa tu movimiento: 1
+-------+-------+-------+
| | | |
| O | 2 | 3 |
| | | |
+-------+-------+-------+
| | | |
| 4 | X | 6 |
| | | |
+-------+-------+-------+
| | | |
| 7 | 8 | 9 |
| | | |
+-------+-------+-------+
+-------+-------+-------+
| | | |
| O | X | 3 |
| | | |
+-------+-------+-------+
| | | |
| 4 | X | 6 |
| | | |
+-------+-------+-------+
| | | |
| 7 | 8 | 9 |
| | | |
+-------+-------+-------+
Ingresa tu movimiento: 8
+-------+-------+-------+
| | | |
| O | X | 3 |
| | | |
+-------+-------+-------+
| | | |
| 4 | X | 6 |
| | | |
+-------+-------+-------+
| | | |
| 7 | O | 9 |
| | | |
+-------+-------+-------+
+-------+-------+-------+
| | | |
| O | X | 3 |
| | | |
+-------+-------+-------+
| | | |
| 4 | X | X |
| | | |
+-------+-------+-------+
| | | |
| 7 | O | 9 |
| | | |
+-------+-------+-------+
Ingresa tu movimiento: 4
+-------+-------+-------+
| | | |
| O | X | 3 |
| | | |
+-------+-------+-------+
| | | |
| O | X | X |
| | | |
+-------+-------+-------+
| | | |
| 7 | O | 9 |
| | | |
+-------+-------+-------+
+-------+-------+-------+
| | | |
| O | X | X |
| | | |
+-------+-------+-------+
| | | |
| O | X | X |
| | | |
+-------+-------+-------+
| | | |
| 7 | O | 9 |
| | | |
+-------+-------+-------+
Ingresa tu movimiento: 7
+-------+-------+-------+
| | | |
| O | X | X |
| | | |
+-------+-------+-------+
| | | |
| O | X | X |
| | | |
+-------+-------+-------+
| | | |
| O | O | 9 |
| | | |
+-------+-------+-------+
¡Has Ganado!

Requerimientos
Implementa las siguientes características:

• El tablero debe ser almacenado como una lista de tres elementos, mientras que
cada elemento es otra lista de tres elementos (la lista interna representa las filas)
de manera que todos los cuadros puedas ser accedidos empleado la siguiente
sintaxis:

board[row][column]

• 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):
# 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.

def DrawMove(board):
# La función dibuja el movimiento de la máquina y actualiza el tablero.
4.7.2.2 Terminación del Módulo

¡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 4:

• La definición y uso de funciones: su razón de ser, propósito, convenciones y


trampas.
• El concepto de cómo pasar argumentos de diferentes maneras y cómo 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.
• Exceptiones.

Ahora estás listo para tomar el cuestionario 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.

Fundamentos de Python 1 Completado

¡Felicidades!
Has completado Fundamentos de Python 1

¡Bien hecho! Has llegado al final del curso Fundamentos de Python 1, y has completado
un paso importante en tu educación de programación en Python.

Ahora estás preparado para tomar el desafío final, el Examen Global, que te ayudará a
revisar la información más importante que has leído y probar las habilidades y
conocimientos que has adquirido a lo largo del curso.
Habiendo completado el curso, también estás preparado para presentar el examen de la
certificación PCEP - Certified Entry-Level Python Programmer, el cual es un paso
intermedio para la certificación PCAP - Certified Associate in Python Programming, así
como el punto de partida para iniciar una carrera en desarrollo de software,
programación en Python y tecnologías relacionadas.

¿Listo?
Fundamentos de Python 2 - Módulo 1

Fundamentos de Python 2:
Módulo 1
Módulos, Paquetes y PIP

En este módulo, aprenderás como:

• Importar y usar módulos de Python.


• Emplear algunos de los módulos más útiles de la biblioteca estándar de Python.
• Construir y usar paquetes de Python
• PIP (Instalador de Paquetes de Python) y cómo usarlo para instalar y desinstalar
paquetes listos para usar de PyPI.

1.1.1.1 Módulos

¿Qué es un módulo?
El código de computadora tiene una tendencia a crecer. Podemos decir que el código que
no crece probablemente sea completamente inutilizable o esté abandonado. Un código
real, deseado y ampliamente utilizado se desarrolla continuamente, ya que tanto las
demandas de los usuarios como sus expectativas se desarrollan de manera diferente.

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 pequeña).

Además, cuando se espera que el código que se está creando sea realmente grande
(puedes usar el número total de líneas de código como una medida útil, pero no muy
precisa, del tamaño del código) entonces, se deseará, o más bien, habrá la necesidad de
dividirlo en muchas partes, implementado en paralelo por unos cuantos, una docena,
varias docenas o incluso varios cientos de desarrolladores.

Por supuesto, esto no se puede hacer usando un archivo fuente grande, el cual esta
siendo editado por todos los programadores al mismo tiempo. Esto seguramente
conducirá a un desastre.
Si se desea que dicho proyecto de software se complete con éxito, se deben tener los
medios que permitan:

• Dividir todas las tareas entre los desarrolladores.


• Después, 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. Los módulos son la respuesta.

1.1.1.2 Empleado Módulos

¿Cómo hacer uso de un módulo?


Entonces, ¿qué es un módulo? El Tutorial de Python lo define como un archivo que
contiene definiciones y sentencias de Python, que se pueden importar más tarde y utilizar
cuando sea necesario.

El manejo de los módulos consta de dos cuestiones diferentes:

• El primero (probablemente el más


común) ocurre cuando se desea
utilizar un módulo ya existente,
escrito por otra persona o creado
por el programador mismo en algún
proyecto complejo: en este caso, se
considera al programador como
el usuario del módulo.
• El segundo ocurre cuando se desea
crear un nuevo módulo, ya sea para
uso propio o para facilitar la vida de
otros programadores: aquí tu eres el proveedor del módulo.

Discutamos ambos 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 "equipamiento
adicional 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 como acceder a
un módulo en particular, se puede utilizar cualquiera de las entidades que almacena.

Comencemos la discusión con uno de los módulos más utilizados, el que lleva por
nombre math . Su nombre habla por sí mismo: el módulo contiene una rica colección de
entidades (no solo funciones) que permiten a un programador implementar
efectivamente cálculos que exigen el uso de funciones matemáticas, como sen() o log().

1.1.1.3 Empleado Módulos

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 clave 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 seno).

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 como se haya realizado la importación.

La forma más sencilla de importar un módulo en particular es usar la instrucción para


importar de la siguiente manera:

import math

La cláusula contiene:

• La palabra reservada import .


• El nombre del módulo que se va a importar.

La instrucción puede colocarse en cualquier parte del código, pero debe colocarse antes
del primer uso de cualquiera de las entidades del módulo.

Si se desea (o se tiene que) importar más de un módulo, se puede hacer repitiendo la


cláusula import , o listando los módulos despues de la palabra reservada import , por
ejemplo:

import math
import sys

o enumerando los módulos después de la palabra clave reservada import , como aquí:

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.


1.1.1.4 Empleado Módulos

Importando un módulo: continuación


Para continuar, debes familiarizarte con un término importante: namespace. No te
preocupes, no entraremos en detalles: esta explicación será lo más breve posible.

Un namespace es un espacio (entendido en un contexto no físico) en el que existen


algunos nombres y los nombres no entran en conflicto entre sí (es decir, no hay dos
objetos diferentes con el mismo nombre). Podemos decir que cada grupo social es un
namespace - el grupo tiende a nombrar a cada uno de sus miembros de una manera
única (por ejemplo, los padres no darían a sus hijos el mismo nombre).

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 número de Seguro Social de EE. UU. 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 como funciona y
como 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 puedes tener tus propias entidades llamadas sin o pi y no serán
afectadas en alguna manera por la importación.
En este punto, es posible que te estes preguntando como acceder al pi el cual viene del
módulo math .

Para hacer esto, se debe de mandar llamar el pi con el nombre del módulo original.

1.1.1.5 Importando un módulo | math

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.
• 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((½π).

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 del nombre del módulo 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
import math
print(math.sin(math.pi/2))

1.1.1.6 Importando un módulo | math

Importando un módulo: continuación


Ahora te mostraremos cómo pueden dos namespaces (el tuyo y el del módulo) pueden
coexistir.

Echa un vistazo al ejemplo en la ventana del editor.

Hemos definido el nuestro propio pi y sin aquí.

Ejecuta el programa. El código debe producir la siguiente salida:

0.99999999
1.0

salida

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

1.1.1.7 Importando un módulo | math

Importando un módulo: continuación


En el segundo método, la sintaxis del import señala con precisión que entidad (o
entidades) del módulo son aceptables en el código:

from math import pi


La instrucción consta de los siguientes elementos:

• La palabra clave reservada from .


• El nombre del módulo a ser (selectivamente) importado.
• La palabra clave 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 únicas que son importadas del módulo indicado.
• Los nombres de las entidades importadas pueden ser accedidas dentro del código
sin especificar el nombre del módulo de origen.

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í está:

from math import sin, pi

print(sin(pi/2))

El resultado debe de ser el mismo que el anterior, se han empleado las mismas
entidades: 1.0 . Copia y pega el código en el editor, y ejecuta el programa.

¿El código parece más simple? Quizás, pero el aspecto no es el único efecto de este tipo
de importación. Veamos más a detalle esto.

1.1.1.8 Importando un módulo | math

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íneas 05 a la 12: redefinen el significado de pi y sin - en efecto, reemplazan
las definiciones originales (importadas) dentro del namespace del código.
• La línea 15: retorna 0.99999999 , lo cual confirma nuestras conclusiones.

Hagamos otra prueba. Observa el código a continuación:

1 pi = 3.14
2
3
4 def sin(x):
5 if 2 * x == pi:
6 return 0.99999999
7 else:
8 return None
9
10
11 print(sin(pi / 2))
12
13 from math import sin, pi
14
15 print(sin(pi / 2))

Aquí, se ha invertido la secuencia de las operaciones del código:

• Las líneas del 01 al 08: definen nuestro propio pi y sin .


• La línea 11: hace uso de ellas (0.99999999 aparece en pantalla).
• La línea 13: lleva a cabo la importación - los símbolos importados reemplazan sus
definiciones anteriores dentro del namespace.
• La línea 15: 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))
1.1.1.9 Importando un módulo | * y as

Importando un módulo: *
En el tercer método, la sintaxis del import es una forma más agresiva que la presentada
anteriormente:

from module import *

Como puedes ver, el nombre de una entidad (o la lista de nombres de entidades) se


reemplaza con un solo asterisco ( * ).

Tal instrucción importa todas las entidades del módulo indicado.

¿Es conveniente? Sí, lo es, ya que libera del deber de enumerar todos los nombres que se
necesiten.

¿Es inseguro? Sí, a menos que conozcas 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 clave reservada as


Si se importa un módulo y no se esta conforme con el nombre del módulo en particular
(por ejemplo, si es el mismo que el de una de sus entidades ya definidas) puede dársele
otro nombre: esto se llama aliasing o renombrado.

Aliasing (renombrado) hace que el módulo se identifique con un nombre diferente al


original. Esto también puede acortar los nombres originales.

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 clave reservada.

from module import *


1.1.1.10 Importando un módulo | aliasing

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 name as alias

Como anteriormente, el nombre original (sin alias) se vuelve inaccesible.

La frase name as alias puede repetirse: puedes emplear comas para separar las frases,
como a continuación:

from module import n as a, m as b, o as c

El ejemplo puede parecer un poco extraño, pero funciona:

from math import pi as PI, sin as sine


print(sine(PI/2))

Ahora estás familiarizado con los conceptos básicos del uso de módulos. Permítenos
mostrarte algunos módulos y algunas de sus entidades útiles.

import math as m

print(m.sin(m.pi/2))
1.1.1.11 RESUMEN DE SECCIÓN

Puntos Claves
1. Si deseas importar un módulo como un todo, puedes hacerlo usando la
sentencia import nombre_del_módulo . Puedes importar más de un módulo a la vez
utilizando una lista separada por comas. Por ejemplo:

import mod1
import mod2, mod3, mod4

Aunque la última forma no se recomienda por razones estilísticas, y es mejor y más


bonito expresar la misma intención de una forma más detallada y explícita, como por
ejemplo:

import mod2
import mod3
import mod4

2. Si un módulo se importa de la manera anterior y desea acceder a cualquiera de sus


entidades, debes anteponer el nombre de la entidad empleando la notación con punto.
Por ejemplo:

import my_module

result = my_module.my_function(my_module.my_data)

El fragmento de código utiliza dos entidades que provienen del módulo my_module : una
función llamada my_function() y una variable con el nombre my_data . Ambos
nombres deben tener el prefijo my_module. Ninguno de los nombres de entidad
importados entra en conflicto con los nombres idénticos existentes en el namespace de
tu código.

3. Se te permite no solo importar un módulo como un todo, sino también importar solo
entidades individuales de él. En este caso, las entidades importadas no deben
especificar el prefijo cuando son empleadas. Por ejemplo:

from module import my_function, my_data

result = my_function(my_data)

La forma anterior, a pesar de su atractivo, no se recomienda debido al peligro de causar


conflictos con los nombres derivados de la importación del namespace del código.

4. La forma más general de la sentencia anterior te permite importar todas las


entidades ofrecidas por un módulo:

from my_module import *


result = my_function(my_data)

Nota: la variante de esta importación no se recomienda debido a las mismas razones que
antes (la amenaza de un conflicto de nombres es aún más peligrosa aquí).

5. Puede cambiar el nombre de la entidad importada "sobre la marcha" utilizando la


frase as del import . Por ejemplo:

from module import my_function as fun, my_data as dat

result = fun(dat)

Ejercicio 1

Quieres invocar la función make_money() contenida en el módulo llamado mint . Tu


código comienza con la siguiente línea:

import mint

¿Cuál es la forma adecuada de invocar a la función?

Revisar

mint.make_money()

Ejercicio 2

Quieres invocar la función make_money() contenida en el módulo llamado mint . Tu


código comienza con la siguiente línea:

from mint import make_money

¿Cuál es la forma adecuada de invocar a la función?

Revisasr

make_money()

Ejercicio 3

Has escrito una función llamada make_money por tu cuenta. Necesitas importar una
función con el mismo nombre del módulo mint y no deseas cambiar el nombre de
ninguno de tus nombres previamente definidos. ¿Qué variante de la
sentencia import puede ayudarte con el problema?

Revisar

# solución de muestra
from mint import make_money as make_more_money

Ejercicio 4

¿Qué forma de invocación de la función make_money es válida si tu código comienza con


la siguiente línea?

from mint import *

Revisar

make_money()

1.2.1.1 Módulos Útiles

Trabajando con módulos estándar


Antes de comenzar a revisar algunos módulos estándar de Python, veamos la
función dir() . No tiene nada que ver con el comando dir de las terminales de Windows
o Unix. El comando dir() no muestra el contenido de un directorio o carpeta de disco,
pero no se puede negar que hace algo similar: puede revelar todos los nombres
proporcionados a través de un módulo en particular.

Existe 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, debes usar el alias, no el nombre original.

Usar la función dentro de un script normal no tiene mucho sentido, pero aún así, es
posible.

Por ejemplo, se puede ejecutar el siguiente código para imprimir los nombres de todas
las entidades dentro del módulo math :

import math

for name in dir(math):


print(name, end="\t")
El código del 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

salida

¿Has notado los nombres extraños que comienzan con __ al inicio de la lista? Se hablará
más sobre ellos cuando hablemos sobre los problemas relacionados con la escritura de
módulos propios.

Algunos de los nombres pueden traer recuerdos de las lecciones de matemáticas, y


probablemente no tendrás ningún problema en adivinar su significado.

El emplear la función dir() dentro de un código puede no parecer muy útil; por lo
general, se desea conocer el contenido de un módulo en particular antes de escribir y
ejecutar el código.

Afortunadamente, se puede ejecutar la función directamente en la consola de


Python (IDLE), sin necesidad de escribir y ejecutar un script por separado.

Así es como se puede hacer:

import math
dir(math)

Deberías de ver algo similar a esto:


1.2.1.2 Módulos Útiles | math

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. Tómate 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).

Por supuesto, 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 (verifican que sea correcto) y devuelven una
medida de un ángulo en radianes.

Para trabajar eficazmente con mediciones de ángulos, el módulo math proporciona las
siguientes entidades:

• pi → una constante con un valor que es una aproximación de π.


• radians(x) → una función que convierte x de grados a radianes.
• degrees(x) → una función que convierte x 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.)
print(ar == pi / 2.)
print(sin(ar) / cos(ar) == tan(ar))
print(asin(sin(ar)) == ar)

1.2.1.3 Módulos Útiles | math

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) → encuentra el valor de xy (toma en cuenta los dominios).

Esta es una función incorporada y no se tiene que importar.

Observa el código en el editor. ¿Puedes predecir su salida?

from math import e, exp, log

print(pow(e, 1) == exp(log(e)))
print(pow(2, 2) == exp(2 * log(2)))
print(log(e, e) == exp(0))
1.2.1.4 Módulos Útiles | math

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) y (y) (lo mismo que sqrt(pow(x, 2)
+ pow(y, 2)) pero más preciso).

Observa el código en el editor. Analiza el programa cuidadosamente.

Demuestra las diferencias fundamentales entre ceil() , floor() y trunc() .

Ejecuta el programa y verifica su salida.

from math import ceil, floor, trunc

x = 1.4
y = 2.6

print(floor(x), floor(y))
print(floor(-x), floor(-y))
print(ceil(x), ceil(y))
print(ceil(-x), ceil(-y))
print(trunc(x), trunc(y))
print(trunc(-x), trunc(-y))

1.2.1.5 Módulos Útiles | random

¿Existe aleatoriedad real en las computadoras?


Otro módulo que vale la pena mencionar es el que se llama random .

Ofrece algunos mecanismos que permiten operar con números pseudoaleatorios.


Toma en cuenta el prefijo pseudo - los números generados por los módulos pueden
parecer aleatorios en el sentido de que no se pueden predecir, pero no hay que olvidar
que todos se calculan utilizando algoritmos muy refinados.

Los algoritmos no son aleatorios, son deterministas y predecibles. Solo aquellos procesos
físicos que se salgan completamente de nuestro control (como la intensidad de la
radiación cósmica) pueden usarse como fuente de datos aleatorios reales. Los datos
producidos por computadoras deterministas no pueden ser aleatorios de ninguna
manera.

Un generador de números aleatorios toma un valor llamado semilla, lo trata como un


valor de entrada, calcula un número "aleatorio" basado en él (el método depende de un
algoritmo elegido) y produce una nueva semilla.

La duración de un ciclo en el que todos los valores semilla son únicos puede ser muy
largo, pero no es infinito: tarde o temprano los valores iniciales comenzarán a repetirse y
los valores generadores también se repetirán. Esto es normal. Es una característica, no un
error.

El valor de la semilla inicial, establecido durante el inicio del programa, determina el


orden en que aparecerán los valores generados.

El factor aleatorio del proceso puede ser aumentado al establecer la semilla tomando un
número de la hora actual - esto puede garantizar que cada ejecución 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.

1.2.1.6 Módulos Útiles | random

Funciones seleccionadas del módulo random


La función 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 a continuación producirá cinco valores pseudoaleatorios, ya que


sus valores están determinados por el valor semilla actual (bastante impredecible), no
puedes adivinarlos:

from random import random

for i in range(5):
print(random())

Ejecuta el programa. Esto es lo que tenemos:

0.9535768927411208
0.5312710096244534
0.8737691983477731
0.5896799172452125
0.02116716297022092
salida de muestra

La función seed

La función seed() es capaz de directamente establecer la semilla del generador. Te


mostramos dos de sus variantes:

• seed() - establece la semilla con la hora actual.


• seed(int_value) - establece la semilla con el valor entero int_value .

Hemos modificado el programa anterior; de hecho, hemos eliminado cualquier rastro de


aleatoriedad del código:

from random import random, seed

seed(0)

for i in range(5):
print(random())

Debido al hecho de que la semilla siempre se establece con el mismo valor, la secuencia
de valores generados siempre se ve igual.

Ejecuta el programa. Esto es lo que tenemos:

0.844421851525
0.75795440294
0.420571580831
0.258916750293
0.511274721369
salida de muestra

¿Y tú?

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

1.2.1.7 Módulos Útiles | random

Funciones seleccionadas del módulo random: continuación


Las funciones randrange y randint

Si deseas valores aleatorios enteros, una de las siguientes funciones encajaría mejor:

• randrange(fin)
• randrange(inico, fin)
• randrange(inicio, fin, incremento)
• randint(izquierda, derecha)

Las primeras tres invocaciones generarán un valor entero tomado


(pseudoaleatoriamente) del rango:

• range(fin)
• range(inicio, fin)
• range(inicio, fin, incremento)

Toma en cuenta la exclusión implícita del lado derecho.

La última función es equivalente a randrange(izquierda, derecha+1) - genera el valor


entero i , el cual cae en el rango [izquierda, derecha] (sin exclusión en el lado derecho).

Observa el código en el editor. Este programa generará una línea que consta de tres
ceros y un cero o un uno en el cuarto lugar.

from random import randrange, randint

print(randrange(1), end=' ')


print(randrange(0, 1), end=' ')
print(randrange(0, 1, 1), end=' ')
print(randint(0, 1))
1.2.1.8 Módulos Útiles | random

Funciones seleccionadas del módulo random: continuación


Las funciones anteriores tienen una desventaja importante: pueden producir valores
repetidos incluso si el número de invocaciones posteriores no es mayor que el rango
especificado.

Observa el código en el editor. Es muy probable que el programa genere un conjunto de


números en el que algunos elementos no sean únicos.

from random import randint

for i in range(10):
print(randint(1, 10), end=',')

Esto es lo que se obtuvo al ejecutarlo:

9,4,5,4,5,8,9,4,8,4,
salida de muestra

Las funciones choice y sample

Como puedes ver, esta no es una buena herramienta para generar números para la
lotería. Afortunadamente, existe una mejor solución que escribir tu propio código para
verificar la singularidad de los números "sorteados".

Es una función con el nombre de choice :

• choice(secuencia)
• sample(secuencia, elementos_a_elegir=1)

La primera variante elige un elemento "aleatorio" de la secuencia de entrada y lo


devuelve.

El segundo crea una lista (una muestra) que consta del


elemento elementos_a_elegir (que por defecto es 1 ) "sorteado" de la secuencia de
entrada.

En otras palabras, la función elige algunos de los elementos de entrada, devolviendo una
lista con la elección. Los elementos de la muestra se colocan en orden aleatorio. Nota
que elementos_a_elegir no debe ser mayor que la longitud de la secuencia de entrada.

Observa el código a continuación:

from random import choice, sample


my_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

print(choice(my_list))
print(sample(my_list, 5))
print(sample(my_list, 10))

Nuevamente, la salida del programa no es predecible. Nuestros resultados se ven así:

4
[3, 1, 8, 9, 10]
[10, 8, 5, 1, 6, 4, 3, 9, 7, 2]
salida

1.2.1.9 Módulos Útiles | platform

¿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 como 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 (esto
puede recordarte una cadena de mando).
• 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 para ti.

1.2.1.10 Módulos Útiles | platform

Funciones seleccionadas del módulo platform


La función platform

El módulo platform permite acceder a los datos de la plataforma subyacente, es decir,


hardware, sistema operativo e información sobre la versión del intérprete.

Existe también una función que puede mostrar todas las capas subyacentes en un solo
vistazo, llamada platform . Simplemente devuelve una cadena que describe el entorno;
por lo tanto, su salida está más dirigida a los humanos que al procesamiento
automatizado (lo verás pronto).

Así es como se puede invocar:

platform(aliased = False, terse = False)


Y ahora:

• aliased → cuando se establece a True (o cualquier valor distinto a 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 a 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
salida

Intel x86 + Gentoo Linux (64 bit):

Linux-3.18.62-g6-x86_64-Intel-R-_Core-TM-_i3-2330M_CPU_@_2.20GHz-
with-gentoo-2.3
Linux-3.18.62-g6-x86_64-Intel-R-_Core-TM-_i3-2330M_CPU_@_2.20GHz-
with-gentoo-2.3
Linux-3.18.62-g6-x86_64-Intel-R-_Core-TM-_i3-2330M_CPU_@_2.20GHz-
with-glibc2.3.4
salida

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
salida

También puedes ejecutar el programa en el IDLE de tu máquina local para verificar que
salida tendrá.

from platform import platform

print(platform())
print(platform(1))
print(platform(0, 1))
1.2.1.11 Módulos Útiles | platform

Funciones seleccionadas del módulo platform:


continuación
La función machine

En ocasiones, 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:

Intel x86 + Windows ® Vista (32 bit):

x86
salida

Intel x86 + Gentoo Linux (64 bit):

x86_64
salida

Raspberry PI2 + Raspbian Linux (32 bit):

armv7l
salida

from platform import machine

print(machine())

1.2.1.12 Módulos Útiles | platform

Funciones seleccionadas del módulo platform:


continuación
La función processor

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
salida

Intel x86 + Gentoo Linux (64 bit):

Intel(R) Core(TM) i3-2330M CPU @ 2.20GHz


salida

Raspberry PI2 + Raspbian Linux (32 bit):

armv7l
salida

Prueba esto en tu máquina local.

from platform import processor


print(processor())

1.2.1.13 Módulos Útiles | platform

Funciones seleccionadas del módulo platform:


continuación
La función system

Una función llamada system() devuelve el nombre genérico del sistema operativo en
una cadena.

Nuestras plataformas de ejemplo se presentan de la siguiente manera:

Intel x86 + Windows ® Vista (32 bit):

Windows
salida

Intel x86 + Gentoo Linux (64 bit):

Linux
salida

Raspberry PI2 + Raspbian Linux (32 bit):

Linux
salida

from platform import system


print(system())

1.2.1.14 Módulos Útiles | platform

Funciones seleccionadas del módulo platform:


continuación
La función version

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
salida

Intel x86 + Gentoo Linux (64 bit):

#1 SMP PREEMPT Fri Jul 21 22:44:37 CEST 2017


salida

Raspberry PI2 + Raspbian Linux (32 bit):

#1 SMP Debian 4.4.6-1+rpi14 (2016-05-05)


salida

from platform import version

print(version())

1.2.1.15 Módulos Útiles | platform

Funciones seleccionadas del módulo platform:


continuación
Las funciones python_implementation y python_version_tuple

Si necesitas saber que versión de Python está ejecutando tu código, puedes verificarlo
utilizando una serie de funciones dedicadas, aquí hay dos de ellas:
• python_implementation() → devuelve una cadena que denota la
implementación de Python (espera CPython aquí, a menos que decidas utilizar
cualquier rama de Python no canónica).
• python_version_tuple() → devuelve una tupla de tres elementos la cual
contiene:
o La parte mayor de la versión de Python.
o La parte menor.
o El número del nivel de parche.

Nuestro programa de ejemplo produjo el siguiente resultado:

CPython
3
7
7
salida de muestra

Es muy probable que tu versión de Python sea diferente.

from platform import python_implementation, python_version_tuple

print(python_implementation())

for atr in python_version_tuple():


print(atr)

1.2.1.16 Módulos Útiles

Índice de Módulos 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.

En la siguiente sección veremos algo más. Te mostraremos cómo escribir tu propio


módulo.

1.2.1.17 RESUMEN DE SECCIÓN

Puntos Clave

1. Una función llamada dir() puede mostrarte una lista de las entidades contenidas
dentro de un módulo importado. Por ejemplo:

import os
dir(os)
Imprime una lista de todo el contenido del módulo os el cual, puedes usar en tu código.

2. El módulo math contiene más de 50 funciones y constantes que realizan operaciones


matemáticas (como sine() , pow() , factorial() ) o aportando valores importantes
(como π y la constante de Euler e).

3. El módulo random agrupa más de 60 entidades diseñadas para ayudarte a usar


números pseudoaleatorios. No olvides el prefijo "pseudo", ya que no existe un número
aleatorio real cuando se trata de generarlos utilizando los algoritmos de la computadora.

4. El módulo platform contiene alrededor de 70 funciones que te permiten sumergirte


en las capas subyacentes del sistema operativo y el hardware. Usarlos te permite
aprender más sobre el entorno en el que se ejecuta tu código.

5. El Índice de Módulos de Python (https://docs.python.org/3/py-modindex.html es un


directorio de módulos impulsado por la comunidad disponible en el universo de Python.
Si deseas encontrar un módulo que se adapte a tus necesidades, comienza tu búsqueda
allí.

Ejercicio 1
¿Cuál es el valor esperado de la variable result después de que se ejecuta el siguiente
código?
import math
result = math.e == math.exp(1)

Revisar

True

Ejercicio 2
(Completa el enunciado) Establecer la semilla del generador con el mismo valor cada vez
que se ejecuta tu programa garantiza que ...

Revisar

... los valores pseudoaleatorios emitidos desde el módulo random serán exactamente los
mismos.

Ejercicio 3
¿Cuál de las funciones del módulo platform utilizarías para determinar el nombre del
CPU que corre dentro de tu computadora?

Revisar

La función processor()
Ejercicio 4
¿Cuál es el resultado esperado del siguiente fragmento de código?
import platform

print(len(platform.python_version_tuple()))

Revisar

1.3.1.1 Módulos y Paquetes

¿Qué es un paquete?
Escribir tus propios módulos no difiere mucho de escribir scripts comunes.

Existen algunos aspectos específicos que se deben tomar en cuenta, pero definitivamente
no es algo complicado. Lo verás pronto.

Resumamos algunos aspectos importantes:

• Un módulo es un contenedor lleno de funciones - puedes empaquetar tantas


funciones como desees en un módulo y distribuirlo por todo el mundo.
• Por supuesto, no es una buena idea mezclar funciones con diferentes áreas de
aplicación dentro de un módulo (al igual que en una biblioteca: nadie espera que
los trabajos científicos se incluyan entre los cómics), así que se deben agrupar las
funciones cuidadosamente y asignar un nombre claro e intuitivo al módulo que
las contiene (por ejemplo, no le des el nombre videojuegos a un módulo que
contiene funciones destinadas a particionar y formatear discos duros).
• Crear muchos módulos puede causar desorden: tarde que temprano
querrás agrupar tus módulos de la misma manera que previamente has agrupado
funciones: ¿Existe un contenedor más general que un módulo?
• Sí lo hay, es un paquete: en el mundo de los módulos, un paquete juega un papel
similar al de una carpeta o directorio en el mundo de los archivos.
1.3.1.2 Módulos y Paquetes

Tu primer módulo: paso 1


En esta sección, trabajarás localmente en tu máquina. Comencemos desde cero. Crea un
archivo vacío, de la siguiente manera:

1
2
module.py

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 código pronto.

El archivo lleva por nombre module.py. No muy creativo, pero es simple y claro.

Tu primer módulo: paso 2


El segundo archivo contiene el código que utiliza el nuevo módulo. Su nombre
es main.py. Su contenido es muy breve hasta ahora:

1 import module
2
main.py
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 (o cualquier otro que prefieras) y ejecuta el archivo main.py. ¿Qué es lo que
ves?

No deberías ver nada. Esto significa que Python ha importado con éxito el contenido del
archivo module.py.

No importa que el módulo esté vacío por ahora. El primer paso ya está hecho, pero antes
de dar el siguiente paso, queremos que eches un vistazo a la carpeta en la que se
encuentran ambos archivos.

¿Notas algo interesante?

Ha aparecido una nueva subcarpeta, ¿puedes verla? Su nombre es __pycache__. Echa


un vistazo adentro. ¿Qué es lo 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 8 si utilizas Python 3.8).

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 el uso de
Python.

Cuando Python importa un módulo por primera vez, traduce el contenido a una forma
algo 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 tiene que ser
tomando en cuenta.
1.3.1.3 Módulos y Paquetes

Tu primer módulo: paso 3


Ahora hemos puesto algo en el archivo del módulo:

print("Me gusta ser un módulo.")

module.py

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

¿Qué es lo que pasa? Deberías de ver la siguiente línea dentro de tu consola:

Me gusta ser un módulo.


salida

Tu primer módulo: paso 4


Volvamos al archivo main.py:
import module

main.py

Ejecuta el archivo. ¿Qué ves? Con suerte, verás algo como esto:

Me gusta ser un módulo.


salida

¿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 sólo una vez, cuando se produce la primera importación,
por lo que las asignaciones realizadas por el módulo no se repiten innecesariamente.

Imagina el siguiente contexto:

• Existe un módulo llamado mod1.


• Existe un módulo llamado mod2 el cual contiene la instrucción import mod1 .
• Hay un archivo principal que contiene las instrucciones import mod1 e 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.

Tu primer módulo: paso 5


Python puede hacer mucho más que solo importar el módulo. 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 como usarlo. Modifica el módulo un poco:


print("Me gusta ser un módulo.")
print(__name__)

module.py

Ahora ejecuta el archivo module.py. Deberías ver las siguientes líneas:

Me gusta ser un módulo.

__main__
salida

Ahora ejecuta el archivo main.py. ¿Y? ¿Ves lo mismo que nosotros?

Me gusta ser un módulo.

module
salida

Podemos decir que:

• Cuando se ejecuta un archivo directamente, su variable __name__ se establece


a __main__ .
• Cuando un archivo se importa como un módulo, su variable __name__ se
establece al nombre del archivo (excluyendo a .py).

Tu primer módulo: paso 6


Así es como puedes hacer uso de la variable __main__ para detectar el contexto en el
cual se activó tu código:

if __name__ == "__main__":
print("Yo prefiero ser un módulo")
else:
print("Me gusta ser un módulo")

module.py
Sin embargo, existe 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.

1.3.1.4 Módulos y Paquetes

Tu primer módulo: paso 7


Este módulo contendrá dos funciones simples, y si deseas saber cuantas veces se han
invocado las funciones, necesitas un contador inicializado en cero cuando se importe el
módulo.

Puedes hacerlo de esta manera:

counter = 0

if __name__ == "__main__":
print("Yo prefiero ser un módulo")
else:
print("Me gusta ser un módulo")

module.py

Tu primer módulo: paso 8


El introducir tal variable es absolutamente correcto, pero puede causar
importantes efectos secundarios que debes tomar en cuenta.

Analiza el archivo modificado main.py:


import module
print(module.counter)

main.py

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.

Tu primer módulo: paso 9


Bueno. Escribamos un código nuevo en nuestro archivo module.py. El módulo
actualizado está listo aquí:

#!/usr/bin/env python3

""" module.py - Un ejemplo de un módulo en Python """

__counter = 0
def suml(the_list):
global __counter
__counter += 1
the_sum = 0
for element in the_list:
the_sum += element
return the_sum

def prodl(the_list):
global __counter
__counter += 1
prod = 1
for element in the_list:
prod *= element
return prod

if __name__ == "__main__":
print("Yo prefiero ser un módulo, pero puedo realizar algunas
pruebas por ti")
my_list = [i+1 for i in range(5)]
print(suml(my_list) == 15)
print(prodl(my_list) == 120)

module.py

Algunos elementos necesitan explicación:

• La línea que comienza con #! tiene muchos nombres - puede ser


llamada shabang, shebang, hashbang, poundbang o incluso hashpling (no nos
preguntes por qué). El nombre en sí no significa nada aquí, su papel es más
importante. 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 como ejecutar el contenido del
archivo (en otras palabras, que programa debe ejecutarse 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 cuando se ejecuta el archivo de
forma independiente, y se aprovechó esta oportunidad para realizar algunas
pruebas sencillas.
Tu primer módulo: paso 10
Ahora es posible usar el nuevo módulo, esta es una forma de hacerlo:

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

main.py

1.3.1.5 Módulos y Paquetes

Tu primer módulo: paso 11


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.

Renunciemos a esta suposición y 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
¿Cómo lidiar con ello?

Para responder a esta pregunta, tenemos que hablar sobre como Python busca los
módulos. Hay una variable especial (en realidad una lista) que almacena todas las
ubicaciones (carpetas o directorios) que se buscan para encontrar un módulo que ha sido
solicitado por la instrucción import.

Python examina estas carpetas en el orden en que aparecen en la lista: si el módulo no se


puede encontrar en ninguno de estos directorios, la importación falla.

De lo contrario, se tomará en cuenta la primera carpeta que contenga un módulo con el


nombre deseado (si alguna de las carpetas restantes contiene un módulo con ese
nombre, se ignorará).

La variable se llama path (ruta), y es accesible a través del módulo llamado sys . Así es
como puedes verificar su valor:

import sys

for p in sys.path:
print(p)

Hemos ejecutado el código dentro del directorio C:\User\user y esto es lo que


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
salida de muestra
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 (la variable path), es completamente
modificable.

Tu primer módulo: paso 12


Una de las varias soluciones posibles se ve así:

from sys import path

path.append('..\\modules')

import module

zeroes = [0 for i in range(5)]


ones = [1 for i in range(5)]
print(module.suml(zeroes))
print(module.prodl(ones))

main.py

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, debes escapar.
• Hemos utilizado el nombre relativo de la carpeta: esto funcionará si inicia el
archivo main.py directamente desde la 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() .

1.3.1.6 Módulos y Paquetes

Tu primer paquete: paso 1


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:
#! /usr/bin/env python3

""" module: alpha """

def funA():
return "Alpha"

if __name__ == "__main__":
print("Yo prefiero ser un módulo")

alpha.py

Nota: hemos presentado todo el contenido solo para el módulo alpha.py, 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).

Tu primer paquete: paso 2


De repente, alguien se da cuenta de
que estos módulos forman su
propia jerarquía, por lo que
colocarlos a todos en una estructura
plana no será una buena idea.

Después de algo de discusión, el


equipo llega a la conclusión de que
los módulos deben agruparse.
Todos los participantes están de
acuerdo en que la siguiente
estructura de árbol refleja
perfectamente las relaciones
mutuas entre los módulos:

Repasemos esto de abajo hacia arriba:

• El grupo ugly contiene dos módulos: psi y omega.


• El grupo best contiene dos módulos: sigma y tau.
• El grupo good contiene dos módulos: (alpha y beta) y un subgrupo (best).
• El grupo extra contiene dos subgrupos: (good y bad) y un módulo (iota).

¿Se ve mal? De ninguna manera: analiza la estructura cuidadosamente. Se parece a algo,


¿no?

Parece la estructura de un directorio.

Construyamos un árbol que refleje las dependencias proyectadas entre los módulos.

1.3.1.7 Módulos y Paquetes

Tu primer paquete: paso 3


Así es como se ve el árbol actualmente:

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 paquete recientemente creado (piensa en el


como la raíz del paquete), impondrá una regla de nomenclatura que te permitirá
nombrar claramente cada entidad del árbol.

Por ejemplo:

• La ubicación de una función llamada funT() del paquete tau puede describirse
como:

extra.good.best.tau.funT()
• Una función marcada como:

extra.ugly.psi.funP()

Proviene del módulo psi el cual esta almacenado en el subpaquete ugly del
paquete extra.

Tu primer paquete: paso 4


Ahora se deben responder dos preguntas:

• ¿Cómo se transforma este árbol (en realidad, un subárbol) en un paquete real de


Python (en otras palabras, ¿cómo convence a Python de que dicho árbol no es
solo un montón de archivos basura, sino un conjunto de módulos)?
• ¿Dónde se coloca el subárbol para que Python pueda acceder a él?

La primer pregunta tiene una respuesta sorprendente: los paquetes, como los módulos,
pueden requerir inicialización.

La inicialización de un módulo se realiza mediante un código independiente (que no


forma parte de ninguna función) ubicado dentro del archivo del módulo. Como un
paquete no es un archivo, esta técnica es inútil para inicializar paquetes.

En su lugar, debes usar un truco diferente: Python espera que haya un archivo con un
nombre muy exclusivo dentro de la carpeta del paquete: __init__.py.

El contenido del archivo se ejecuta cuando se importa cualquiera de los módulos del
paquete. Si no deseas ninguna inicialización especial, puedes dejar el archivo vacío, pero
no debes omitirlo.

1.3.1.8 Módulos y Paquetes

Tu primer paquete: paso 5


Recuerda: 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, ¿Dónde se coloca el subárbol


para que sea accesible para Python? La respuesta es simple: donde quiera. Solo tienes
que asegurarte de que Python conozca la ubicación del paquete. Ya sabes como hacer
eso.

Estás listo para usar tu primer paquete.


Tu primer paquete: paso 6
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


empleado el archivo main2.py.
1.3.1.9 Módulos y Paquetes

Tu primer paquete: paso 7


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

Así es como se hace:

from sys import path


path.append('..\\packages')

import extra.iota
print(extra.iota.funI())

main2.py

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:


from sys import path
path.append('..\\packages')

from extra.iota import funI


print(funI())

main2.py

Nota el nombre calificado del módulo iota.

1.3.1.10 Módulos y Paquetes

Tu primer paquete: paso 8


Ahora vamos hasta el final del árbol: así es como se obtiene acceso a los
módulos sigma y tau.

from sys import path

path.append('..\\packages')

import extra.good.best.sigma
from extra.good.best.tau import funT

print(extra.good.best.sigma.funS())
print(funT())

main2.py

Puedes hacer tu vida más fácil usando un alias:


from sys import path

path.append('..\\packages')

import extra.good.best.sigma as sig


import extra.good.alpha as alp

print(sig.funS())
print(alp.funA())

main2.py

Tu primer paquete: paso 9


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:

from sys import path

path.append('..\\packages\\extrapack.zip')

import extra.good.best.sigma as sig


import extra.good.alpha as alp
from extra.iota import funI
from extra.good.beta import funB

print(sig.funS())
print(alp.funA())
print(funI())
print(funB())

main2.py
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.

1.3.1.11 RESUMEN DE SECCIÓN

Puntos Clave

1. Mientras que un módulo está diseñado para acoplar algunas entidades relacionadas
como funciones, variables o constantes, un paquete es un contenedor que permite el
acoplamiento de varios módulos relacionados bajo un mismo nombre. Dicho contenedor
se puede distribuir tal cual (como un lote de archivos implementados en un subárbol de
directorio) o se puede empaquetar dentro de un archivo zip.

2. Durante la primera importación del módulo, Python traduce su código fuente a un


formato semi-compilado almacenado dentro de los archivos pyc y los implementa en el
directorio __pycache__ ubicado en el directorio de inicio del módulo.

3. Si deseas decirle al usuario del módulo que una entidad en particular debe tratarse
como privada (es decir, no debe usarse explícitamente fuera del módulo), puedes marcar
su nombre con el prefijo _ o __ . No olvides que esto es solo una recomendación, no una
orden.

4. Los nombres shabang, shebang, hasbang, poundbang y hashpling describen el dígrafo


escrito como #! , se utiliza para instruir a los sistemas operativos similares a Unix sobre
cómo se debe iniciar el archivo fuente de Python. Esta convención no tiene efecto en MS
Windows.

5. Si deseas convencer a Python de que debe tomar en cuenta el directorio de un paquete


no estándar, su nombre debe insertarse/agregarse en/a la lista de directorios de
importación almacenada en la variable path contenida en el módulo sys .

6. Un archivo de Python llamado __init__.py se ejecuta implícitamente cuando un


paquete que lo contiene está sujeto a importación y se utiliza para inicializar un paquete
y/o sus subpaquetes (si los hay). El archivo puede estar vacío, pero no debe faltar.

Ejercicio 1
Deseas evitar que el usuario de tu módulo ejecute tu código como un script ordinario.
¿Cómo lograrías tal efecto?

Revisar
import sys

if __name__ == "__main__":
print "¡No hagas eso!"
sys.exit()

Ejercicio 2
Algunos paquetes adicionales y necesarios se almacenan dentro del
directorio D:\Python\Project\Modules . Escribe un código asegurándote de que Python
recorra el directorio para encontrar todos los módulos solicitados.

Revisar

import sys

# ¡Toma en cuenta las diagonales invertidas dobles!


sys.path.append("D:\\Python\\Project\\Modules")

Ejercicio 3
El directorio mencionado en el ejercicio anterior contiene un subárbol con la siguiente
estructura:
abc
|__ def
|__ mymodule.py

Asumiendo que D:\Python\Project\Modules se ha adjuntado con éxito a la


lista sys.path , escribe una directiva de importación que te permita usar todas las
entidades de mymodule .

Revisar

import abc.def.mymodule

1.4.1.1 Instalador de Paquetes de Python (PIP)

El Ecosistema de Paquetes de Python y cómo usarlo


Python es un instrumento muy poderoso; esperamos que ya lo hayas experimentado.
Muchas personas de todo el mundo se sienten así y usan Python de forma regular para
desarrollar lo que pueden hacer en muchos campos de actividad completamente
diferentes. Esto significa que Python se ha convertido en una herramienta
interdisciplinaria empleada en innumerables aplicaciones. No podemos pasar por todas
las esferas en las que Python brillantemente muestra sus habilidades, así que permítenos
contarte las más impresionantes.
En primer lugar, Python se ha convertido en líder en investigación sobre inteligencia
artificial. La minería de datos, una de las disciplinas científicas modernas más
prometedoras, también utiliza Python. Matemáticos, psicólogos, genetistas,
meteorólogos, lingüistas: todas estas personas ya usan Python, o si aún no lo hacen,
estamos seguros de que lo harán muy pronto. No hay forma de escapar de esta
tendencia.

Por supuesto, no tiene ningún sentido hacer que todos los usuarios de Python escriban
su código desde cero, manteniéndolos perfectamente aislados del mundo exterior y de
los logros de otros programadores. Esto sería antinatural y contraproducente.

Lo más preferible y eficiente es permitir que todos los miembros de la comunidad de


Python intercambien libremente sus códigos y experiencias. En este modelo, nadie está
obligado a empezar a trabajar desde cero, ya que existe una alta probabilidad de que
alguien más haya estado trabajando en el mismo problema (o uno muy similar).

Como sabes, Python se creó como software de código abierto, y esto también funciona
como una invitación para que todos los programadores mantengan todo el ecosistema
de Python como un entorno abierto, amigable y libre. Para que el modelo funcione y
evolucione, se deben proporcionar algunas herramientas adicionales, herramientas que
ayuden a los creadores a publicar, mantener y cuidar su código.

Estas mismas herramientas deberían ayudar a los usuarios a hacer uso del código, tanto
el código ya existente como el código nuevo que aparece todos los días. Gracias a eso,
escribir código nuevo para nuevos desafíos no es como construir una casa nueva,
comenzando por los cimientos.

Además, el programador es libre de modificar el código de otra persona para adaptarlo a


sus propias necesidades y, de hecho, crear un producto completamente nuevo que
pueda ser utilizado por otro desarrollador. Afortunadamente, el proceso parece no tener
fin.

Para hacer girar este mundo, se deben establecer y mantener en movimiento dos
entidades básicas: un repositorio centralizado de todos los paquetes de software
disponibles; y una herramienta que permite a los usuarios acceder al repositorio . Ambas
entidades ya existen y se pueden utilizar en cualquier momento.
1.4.1.2 Instalador de Paquetes de Python (PIP)

El Ecosistema de Paquetes de Python y cómo usarlo:


continuación
El repositorio (o repo para abreviar) que mencionamos se llama PyPI (es la abreviatura de
Python Package Index) y lo mantiene un grupo de trabajo llamado Packaging Working
Group, una parte de la Python Software Foundation, cuya tarea principal es apoyar a los
desarrolladores de Python en la diseminación de código eficiente.

Puedes encontrar su sitio web aquí:


https://wiki.python.org/psf/PackagingWG.

El sitio web de PyPI (administrado por PWG) se encuentra en la dirección:


https://pypi.org/.

En Julio de 2021 fuimos al sitio mencionado, y descubrimos que PyPI albergaba 315,000
proyectos, que constan de más de 4,500,000 archivos administrados por 520,000
usuarios.

Estos tres números por sí solos muestran claramente la potencia de la comunidad de


Python y la importancia de la cooperación entre desarrolladores.

Debemos señalar que PyPI no es el único repositorio de Python existente. Por el


contrario, hay muchos de ellos, creados para proyectos y dirigidos por muchas
comunidades Python más grandes y más pequeñas. Es probable que algún día tu y tus
colegas quieran crear sus propios repositorios.

De todos modos, PyPI es el repositorio de Python más importante del mundo. Si


modificamos un poco el dicho clásico, podemos afirmar que "todos los caminos de
Python conducen a PyPl", y eso no es una exageración.
1.4.1.3 Instalador de Paquetes de Python (PIP)

El Repositorio de PyPI: la Tienda de Quesos


El repositorio de PyPI a veces se denomina la Tienda de Quesos.

¿Te suena un poco extraño? No te preocupes, todo es perfectamente inocente.

Nos referimos al repositorio como una tienda, porque vas allí por las mismas razones por
las que vas a otras tiendas: para satisfacer tus necesidades. Si quieres un poco de queso,
ve a la quesería. Si deseas una pieza de software, ve a la tienda de software.
Afortunadamente, la analogía termina aquí: no necesitas dinero para sacar algún
software de la tienda de repositorios.

PyPI es completamente gratuito, puedes tomar un código y usarlo; no encontrarás cajero


ni guardia de seguridad. Por supuesto, esto no te exime de ser cortés y honesto. Debes
obedecer todos los términos de la licencia, así que no olvides leerlos.

OK, se comprende lo de la tienda, pero ¿qué tiene que ver el queso con Python?

The Cheese Shop (La Tienda de Quesos) es uno de los sketches más famosos de Monty
Python. Representa la aventura surrealista de un inglés que intenta comprar queso.
Desafortunadamente, la tienda que visita (llamada inmodestamente Ye National Cheese
Emporium) no tiene queso en existencia.

Por supuesto, está destinado a ser irónico. Como ya sabes, PyPI tiene una gran cantidad
de software en stock y está disponible las 24 horas del día, los 7 días de la semana. Tiene
todo el derecho a identificarse como Ye International Python Software Emporium.
1.4.1.4 Instalador de Paquetes de Python (PIP)

El Repositorio de PyPI: la Tienda de Quesos (continuación)


PyPI es una tienda muy específica, no solo porque ofrece todos sus productos de forma
gratuita. También requiere una herramienta especial para hacer uso de ella.

Afortunadamente, esta herramienta también es gratuita, por lo que si deseas hacer tu


propia hamburguesa con queso digital utilizando los productos que ofrece PyPI Shop,
necesitarás una herramienta gratuita llamada pip.

No, no has escuchado mal. Solo pip. Es otro acrónimo, claro, pero su naturaleza es más
compleja que el PyPI mencionado anteriormente, ya que es un ejemplo de acrónimo
recursivo, lo que significa que el acrónimo se refiere a sí mismo, lo que significa que
explicarlo es un proceso infinito.

¿Por qué? Porque pip significa "pip instala paquetes", y el pip dentro de "pip instala
paquetes" significa "pip instala paquetes" y ...

Detengámonos ahí. Gracias por tu cooperación.

Por cierto, hay algunos otros acrónimos recursivos muy famosos. Uno de ellos es Linux,
que se puede interpretar como "Linux no es Unix".
1.4.1.5 Instalador de Paquetes de Python (PIP)

Cómo instalar pip


La pregunta que debería hacerse ahora es: ¿cómo conseguir un cuchillo para un queso
especifico? En otras palabras, ¿cómo asegurarse de que pip esté instalado y listo para
funcionar?

La respuesta más precisa es "depende".

Algunas instalaciones de Python vienen con pip, otras no. Además, no solo depende del
sistema operativo que utilices, aunque este es un factor muy importante.

Comencemos con MS Windows.

1.4.1.6 PIP Instalación en MS Windows

pip en MS Windows
El instalador de Python para MS Windows ya contiene pip, por lo que no es necesario
seguir ningún otro paso para instalarlo. Desafortunadamente, si la variable PATH está
mal configurada, es posible que pip no esté disponible.

Para verificar que no te hemos engañado, intenta hacer esto:

• Abre la consola de Windows (CMD o PowerShell, lo que sea que prefieras)


• Ejecuta el siguiente comando:

• pip --version

• En el escenario más optimista (y realmente queremos que eso suceda) verás algo
como esto:

• La ausencia de este mensaje puede significar que la variable PATH apunta


incorrectamente a la ubicación de los binarios de Python o no apunta a ellos en
absoluto; por ejemplo, nuestra variable PATH contiene la siguiente subcadena:

C:\Program Files\Python3\Scripts\;C:\Program Files\Python3\;

• La forma más fácil de reconfigurar la variable PATH es reinstalar Python,


indicando al instalador que lo configure por ti.

1.4.1.7 PIP Instalación en Linux

pip en Linux
Diferentes distribuciones de Linux pueden comportarse de manera diferente cuando se
trata de usar pip. Algunas de ellas (como Gentoo), que están estrechamente vinculadas a
Python y que lo usan internamente, pueden tener pip preinstalado y están
instantáneamente listas para funcionar.

No olvides que algunas distribuciones de Linux pueden utilizar más de una versión de
Python al mismo tiempo, por ejemplo, un Python 2 y un Python 3 coexistiendo uno al
lado del otro. Estos sistemas pueden iniciar Python 2 como la versión predeterminada y
puede ser necesario especificar explícitamente el nombre del programa como python3.
En este caso, puede haber dos pip diferentes identificados como pip (o pip2) y pip3.
Compruébalo cuidadosamente.

Abre la ventana de la terminal y emite el siguiente comando:

pip --version
Una respuesta similar a la que se muestra en la imagen anterior determina que has
iniciado pip desde Python 2, por lo que el siguiente intento debería verse de la siguiente
manera:

pip3 --version

Como puedes ver, ahora estamos seguros de que estamos utilizando la versión adecuada
de pip.

1.4.1.8 PIP Instalación en Linux

pip en Linux: continuación


Desafortunadamente, algunas distribuciones de Linux no tienen pip preinstalado, incluso
si Python está instalado por defecto (algunas versiones de Ubuntu pueden comportarse
de esta manera). En este caso, tienes dos posibilidades:

• Instalar pip como un paquete del sistema usando un administrador de paquetes


dedicado (por ejemplo, apt en sistemas tipo Debian).
• Instalar pip usando mecanismos internos de Python.

El primero es definitivamente mejor. Aunque hay algunos scripts inteligentes que pueden
descargar e instalar pip ignorando el sistema operativo, te recomendamos que no los
utilices. Este método puede causarte problemas.
Observa, intentamos iniciar pip3 y fallamos. Nuestro sistema operativo (esta vez
usamos Ubuntu Budgie) sugirió usar apt para instalar el paquete llamado python3-pip:

Ese es un buen consejo y lo seguiremos, pero hay que decir que necesitaremos derechos
administrativos para hacerlo. No olvides que diferentes distribuciones de Linux pueden
usar diferentes administradores de paquetes (por ejemplo, podría ser pacman si usas
Arch Linux, o yum usado por distribuciones derivadas de Red Hat).

De cualquier manera, todos estos métodos deberían ayudarte en tener pip (o pip3)
instalado y funcionando.

Observa lo que sucedió cuando seguimos la sugerencia del sistema operativo:


Como puedes ver, el sistema operativo decidió instalar no solo pip en sí, sino también un
par de componentes adicionales necesarios para pip. Esto es normal, no te alarmes.

1.4.1.9 PIP Instalación en Linux

pip en Linux: continuación


Cuando apt termina su trabajo, finalmente podemos utilizar pip3:
Si eres un usuario de Mac y has instalado Python 3 usando el instalador brew, pip ya está
presente en tu sistema y listo para funcionar. Compruébelo emitiendo el comando
mencionado anteriormente:

pip3 --version

y espera la respuesta.

Esto es lo que nosotros vimos:

1.4.1.10 Dependencias

Dependencias
Ahora que estamos seguros de que pip está listo para usarse, vamos a limitar nuestro
enfoque a MS Windows solamente, ya que su comportamiento es (debería ser) el mismo
en todos los sistemas operativos, pero antes de comenzar, debemos explicar un asunto
importante e informarte sobre las dependencias.

Imagina que has creado una brillante aplicación de Python llamada redsuspenders, capaz
de predecir los tipos de cambio de la bolsa de valores con un 99% de precisión (por
cierto, si realmente lo haces, contáctanos de inmediato).

Por supuesto, has utilizado algún código existente para lograr este objetivo, por ejemplo,
tu aplicación importa un paquete llamado nyse que contiene algunas funciones y clases
cruciales. Además, el paquete nyse importa otro paquete llamado wallstreet, mientras
que el paquete wallstreet importa otros dos paquetes esenciales llamados bull y bear.
Como probablemente ya habrás adivinado, las conexiones entre estos paquetes son
cruciales, y si alguien decide usar tu código (pero recuerda, ya lo hemos reclamado
primero) también tendrás que asegurarte de que todos los paquetes requeridos están en
su lugar.

Para abreviar, podemos decir que la dependencia es un fenómeno que aparece cada vez
que vas a utilizar un software que depende de otro software. Ten en cuenta que la
dependencia puede incluir (y generalmente incluye) más de un nivel de desarrollo de
software.

¿Significa esto que un usuario potencial del paquete nyse está obligado a rastrear todas
las dependencias e instalar manualmente todos los paquetes necesarios? Eso sería
horrible, ¿no?

Sí, definitivamente es horrible, por lo que no deberías sorprenderse de que el proceso de


cumplir arduamente con todos los requisitos posteriores tenga su propio nombre, y se
llame infierno de dependencias.

¿Cómo nos ocupamos de eso? ¿Todos los usuarios están condenados a visitar el infierno
para ejecutar el código por primera vez?

Afortunadamente no, pip puede hacer todo esto por ti. Puede descubrir, identificar y
resolver todas las dependencias. Además, puede hacerlo de la manera más inteligente,
evitando descargas y reinstalaciones innecesarias.
1.4.1.11 Cómo usar pip

Cómo usar pip


Ahora estamos listos para preguntarle a pip qué puede hacer por nosotros. Hagámoslo,
emite el siguiente comando:

pip help

y espera la respuesta de pip. Así es como se mira:

No olvides que puedes necesitar reemplazar pip por pip3 si tu entorno lo requiere.

La lista producida por pip resume todas las operaciones disponibles, y la última de ellas
es help , la cual acabamos de usar.

Si deseas saber más sobre cualquiera de las operaciones enumeradas, puedes utilizar la
siguiente forma de invocación de pip:

pip help (operación o comando)

Por ejemplo, la línea:

pip help install

te mostrará información detallada sobre el uso y la parametrización del


comando install .

Si deseas saber qué paquetes de Python se han instalado hasta ahora, puedes usar la
operación list , justo como aquí:

pip list
El resultado que verás es bastante impredecible. No te sorprendas si el contenido de tu
pantalla termina siendo completamente diferente. El nuestro es el siguiente:

Como puedes ver, hay dos columnas en la lista, una muestra el nombre del paquete
instalado y la otra muestra la versión del paquete. No podemos predecir el estado de tu
instalación de Python.

Lo único que sabemos con certeza es que tu lista contiene las dos líneas que vemos en
nuestra lista: pip y setuptools. Esto sucede porque el sistema operativo está convencido
de que un usuario que desee pip probablemente necesitará las setuptools (herramientas
de configuración). No está mal.

1.4.1.12 Cómo usar pip

Cómo usar pip: continuación


La lista pip no es muy informativa y puede suceder que no satisfaga tu curiosidad.
Afortunadamente, hay un comando que puede brindarte más información sobre
cualquiera de los paquetes instalados (ten en cuenta la palabra installed). La sintaxis del
comando es la siguiente:

pip show nombre_del_paquete

Lo usaremos de una manera un poco engañosa: queremos convencer a pip de que


confiese algo sobre sí mismo. Así es como lo hacemos:

pip show pip

Parece un poco extraño, ¿no? A pesar de esto, funciona bien y la autopresentación


de pip parece consistente y actual:
¿Puedes preguntar de dónde provienen estos datos? ¿Es pip realmente tan perceptivo?
En lo absoluto: la información que aparece en la pantalla se toma del interior del paquete
que se muestra. En otras palabras, el creador del paquete está obligado a equiparlo con
todos los datos necesarios (o para expresarlo de manera más precisa: metadatos).

Observa las dos líneas en la parte inferior de la salida. Muestran:

• Qué paquetes son necesarios para utilizar con éxito el paquete ( Requires: ).
• Qué paquetes necesitan que el paquete se utilice con éxito ( Required-by: ).

Como puedes ver, ambas propiedades están vacías. No dudes en intentar utilizar el
comando show en relación con cualquier otro paquete instalado.

El poder de pip proviene del hecho de que en realidad es una puerta de entrada al
universo del software Python. Gracias a eso, puedes navegar e instalar cualquiera de los
cientos de paquetes listos para usar reunidos en los repositorios de PyPI. No olvides
que pip no puede almacenar todo el contenido de PyPI localmente (es innecesario y no
sería económico).

De hecho, pip usa Internet para consultar PyPI y descargar los datos requeridos. Esto
significa que debes tener una conexión de red en funcionamiento siempre que vayas a
solicitar a pip cualquier cosa que pueda implicar interacciones directas con la
infraestructura de PyPI.

Uno de estos casos ocurre cuando deseas buscar en PyPI para encontrar el paquete
deseado. Este tipo de búsqueda se inicia con el siguiente comando:

pip search anystring

La cadena anystring que se proporciono será buscada en:

• Los nombres de todos los paquetes.


• Las cadenas de resumen de todos los paquetes.

Ten en cuenta que algunas búsquedas pueden generar una avalancha real de datos, así
que trata de ser lo más específico posible. Por ejemplo, una consulta de apariencia
inocente como esta:
pip search pip

Produce más de 100 líneas de resultados (pruébalo tu mismo, no te fíes de nuestra


palabra). Por cierto, la búsqueda no distingue entre mayúsculas y minúsculas.

Si no eres fanático de la lectura en consola, puedes usar la forma alternativa de navegar


por el contenido de PyPI que ofrece un motor de búsqueda, disponible
en https://pypi.org/search.

1.4.1.13 Cómo usar pip

Cómo usar pip: continuación


Suponiendo que tu búsqueda es exitosa (o estás decidido a instalar un paquete específico
de un nombre ya conocido), puedes usar pip para instalar el paquete en tu computadora.

Es posible que ahora se pongan en práctica dos escenarios posibles:

• Deseas instalar un nuevo paquete solo para ti; no estará disponible para ningún
otro usuario (cuenta) existente en tu computadora; este procedimiento es el único
disponible si no puedes elevar tus permisos y actuar como administrador del
sistema.
• Has decidido instalar un nuevo paquete para todo el sistema; tienes privilegios de
administrador y no tienes miedo de utilizarlos.

Para distinguir entre estas dos acciones, pip emplea una opción dedicada llamada --
user (observa el guión doble). La presencia de esta opción indica a pip que actúe
localmente en nombre de tu usuario sin privilegios de administrador.

Si no agregas esto, pip asume que eres un administrador del sistema y no hará nada para
corregirlo si no lo eres.

En nuestro caso, vamos a instalar un paquete llamado pygame: es una biblioteca


extendida y compleja que permite a los programadores desarrollar juegos de
computadora usando Python.

El proyecto ha estado en desarrollo desde el año 2000, por lo que es un código maduro y
confiable. Si quieres saber más sobre el proyecto y sobre la comunidad que lo lidera,
visita https://www.pygame.org.

Si eres administrador del sistema, puedes instalar pygame usando el siguiente comando:

pip install pygame

Si no eres un administrador, o no quieres engordar tu sistema operativo instalando


pygame en todo el sistema, puedes instalarlo solo para ti:
pip install --user pygame

Depende de ti cuál de los procedimientos anteriores deseas que se lleve a cabo.

Pip tiene la costumbre de mostrar animaciones textuales sofisticadas que indican el


progreso de la instalación, así que observe la pantalla con atención, ¡no te pierdas el
espectáculo! Si el proceso tiene éxito, verás algo como la imagen anteror.

Te alentamos a usar:

pip show pygame

pip list

para obtener más información sobre lo que realmente sucedió.

1.4.1.14 Cómo usar pip

Cómo usar pip: un programa de prueba simple


Ahora que pygame es finalmente accesible, podemos intentar usarlo en un programa de
prueba muy simple. Comentémoslo brevemente.

• La línea 1: importa pygame y nos permite usarlo.


• La línea 3: el programa se ejecutará mientras la variable run sea True .
• Las líneas 4 y 5: determinan el tamaño de la ventana.
• La línea 6: inicializa el entorno pygame.
• La línea 7: prepara la ventana de la aplicación y establece su tamaño.
• La línea 8: crea un objeto que represente la fuente predeterminada de 48 puntos.
• La línea 9: crea un objeto que represente un texto dado, el texto será suavizado
( True ) y blanco ( 255,255,255 ).
• La línea 10: inserta el texto en el búfer de pantalla (actualmente invisible).
• La línea 11: invierte los búferes de la pantalla para que el texto sea visible.
• La línea 12: el bucle principal de pygame comienza aquí.
• La línea 13: obtiene una lista de todos los eventos pendientes de pygame.
• Las líneas 14 a la 16: revisan si el usuario ha cerrado la ventana o ha hecho clic en
algún lugar dentro de ella o ha pulsado alguna tecla.
• La línea 15: si es así, se deja de ejecutar el código.

import pygame

run = True
width = 400
height = 100
pygame.init()
screen = pygame.display.set_mode((width, height))
font = pygame.font.SysFont(None, 48)
text = font.render("Bienvenido a pygame", True, (255, 255, 255))
screen.blit(text, ((width - text.get_width()) // 2,
(height - text.get_height()) // 2))
pygame.display.flip()
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT\
or event.type == pygame.MOUSEBUTTONUP\
or event.type == pygame.KEYUP:
run = False

1.4.1.15 Cómo usar pip

Cómo usar pip: continuación


Esto es lo que esperamos de nuestro código:
El comando pip install tiene dos habilidades adicionales importantes:

• Es capaz de actualizar un paquete instalado localmente; por ejemplo, si deseas


asegurarte de que estás utilizando la última versión de un paquete en particular,
puedes ejecutar el siguiente comando:

pip install -U nombre_del_paquete

Donde -U significa actualizar. Nota: esta forma del comando hace uso de la opción --
user por el mismo propósito que se presentó anteriormente.

• Es capaz de instalar una versión seleccionada por el usuario de un paquete


(pip instala por defecto la versión más nueva disponible); para lograr este objetivo
debes utilizar la siguiente sintaxis:

pip install nombre_del_paquete==versión_del_paquete

(toma en cuenta el doble signo de igual), por ejemplo:

pip install pygame==1.9.2

1.4.1.16 Cómo usar pip

Cómo usar pip: continuación


Si alguno de los paquetes instalados actualmente ya no es necesario y deseas deshacerte
de el, pip también será útil. Su comando uninstall ejecutará todos los pasos
necesarios.

La sintaxis requerida es clara y simple:

pip uninstall nombre_del_paquete

Así que si ya no quieres pygame, puedes ejecutar el siguiente comando:

pip uninstall pygame

Pip querrá saber si estás seguro de la elección que estás tomando; debes estar
preparado para dar la respuesta correcta.

El proceso se ve así:
1.4.1.17 Cómo usar pip

¡Utiliza pip!
Las capacidades de Pip no terminan aquí, pero el conjunto de comandos que te
presentamos es suficiente para comenzar a administrar con éxito los paquetes que no
forman parte de la instalación regular de Python.

Esperamos haberte animado a realizar tus propios experimentos con pip y el universo de
paquetes de Python. PyPI te invita a sumergirte en sus amplios recursos.

Algunos dicen que una de las virtudes más importantes de la programación es la pereza.
No nos malinterpretes, no queremos que pases todo el día durmiendo una siesta en el
sofá y soñando con código de Python.

Un programador perezoso es un programador que busca soluciones existentes y analiza


el código disponible antes de comenzar a desarrollar su propio software desde cero.

Esta es la razón por la que existen PyPI y pip: ¡utilízalos!

1.4.1.18 RESUMEN DE SECCIÓN

Puntos Claves
1. Un repositorio (o repo para abreviar) diseñado para recopilar y compartir código
Python gratuito lleva por nombre Python Package Index (PyPI) aunque también es
probable que te encuentres con el nombre de The Cheese Shop (La Tienda de Queso). Su
sitio web está disponible en https://pypi.org/.

2. Para hacer uso de The Cheese Shop, se ha creado una herramienta especializada y su
nombre es pip (pip instala paquetes mientras que pip significa ... ok, no importa). Como
es posible que pip no se implemente como parte de la instalación estándar de Python, es
posible que debas instalarlo manualmente. Pip es una herramienta de consola.

3. Para verificar la versión de pip, se deben emitir los siguientes comandos:

pip --version

pip3 --version

Comprueba tu mismo cuál de estos funciona en el entorno de tu sistema operativo.

4. La lista de las actividades principales de pip tiene el siguiente aspecto:

• pip help operación_o_comando – muestra una breve descripción de pip.


• pip list – muestra una lista de los paquetes instalados actualmente.
• pip show nombre_del_paquete – muestra información que incluyen las
dependencias del paquete.
• pip search cadena – busca en los directorios de PyPI para encontrar
paquetes cuyos nombres contengan cadena.
• pip install nombre – instala el paquete nombre en todo el sistema (espera
problemas cuando no tengas privilegios de administrador).
• pip install --user nombre – instala nombre solo para ti; ningún otro
usuario de la plataforma podrá utilizarlo.
• pip install -U nombre – actualiza un paquete previamente instalado.
• pip uninstall nombre – desinstala un paquete previamente instalado.

Ejercicio 1
¿De donde proviene el nombre The Cheese Shop?

Revisar

Es una referencia a un viejo sketch de Monty Python que lleva el mismo nombre.

Ejercicio 2
¿Por qué deberías asegurarte de cuál pip o pip3 es el correcto para ti?

Revisar
Cuando Python 2 y Python 3 coexisten en el sistema operativo, es probable
que pip identifique la instancia de pip que trabaja solo con paquetes de Python 2.

Ejercicio 3
¿Cómo puedes determinar si tu pip funciona con Python 2 o Python 3?

Revisar

pip --version te lo dirá.

Ejercicio 4
Desafortunadamente, no tienes privilegios de administrador. ¿Qué debes hacer para
instalar un paquete en todo el sistema?

Revisar

Tienes que consultar a tu administrador del sistema – ¡no intentes hackear tu sistema
operativo!

1.4.1.19 Finalización del Módulo

¡Felicitaciones! Has completado FP2: Módulo 1.


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

• Trabajar con módulos de Python; importar, crear y usar módulos.


• Utilizar ciertos módulos STL de Python (math, random, y platform).
• Construir y utilizar paquetes en Python.
• PIP (el Instalador de Paquetes de Python).

Ahora estás listo para realizar el cuestionario del módulo e intentar el desafío final: El
Examen del Módulo 1, que te ayudará a evaluar lo que has aprendido hasta ahora.

Fundamentos de Python 2 - Módulo 2

Fundamentos de Python 2:
Módulo 2
Cadenas, Métodos de Listas y Excepciones

En este módulo, aprenderás sobre:

• Caracteres, cadenas y estándares de codificación.


• Cadenas frente a Listas: similitudes y diferencias.
• Métodos de listas.
• Métodos de cadenas.
• Cómo Python maneja los errores en tiempo de ejecución.
• Controlando el flujo de errores mediante try y except .
• La jerarquía de las excepciones.

2.1.1.1 Caracteres y Cadenas frente a las Computadoras

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 numéricos. 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.

¿Cómo es esto posible?

¿Cómo puedes hacerlo en Python? Esto es lo que discutiremos ahora. Comencemos con
como 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
y salida.
Un ejemplo de un espacio en blanco que es completamente invisible a simple vista es un
código especial, o un par de códigos (diferentes sistemas operativos pueden tratar este
asunto de manera diferente), que se utilizan para marcar el final de las líneas dentro de
los archivos de texto.

Las personas no ven este signo (o estos signos), pero pueden observar el efecto de su
aplicación donde ven un salto de línea.

Podemos crear prácticamente cualquier cantidad de asignaciones de números con


caracteres, pero la vida en un mundo en el que cada tipo de computadora utiliza una
codificación de caracteres diferentes no sería muy conveniente. Este sistema ha llevado a
la necesidad de introducir un estándar universal y ampliamente aceptado, implementado
por (casi) todas las computadoras y sistemas operativos en todo el mundo.

El denominado ASCII (por sus siglas en inglés American Standard Code for Information
Interchange). El Código Estándar Americano para Intercambio de Información es el más
utilizado, y es posible suponer que casi todos los dispositivos modernos (como
computadoras, impresoras, teléfonos móviles, tabletas, etc.) usan este código.

El código proporciona espacio para 256 caracteres diferentes, pero solo nos interesan los
primeros 128. Si deseas ver como 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 carácter 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 código es 65. Ahora calcula la diferencia entre el código de la a y la A.
Es igual a 32. Ese es el código 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.

2.1.1.2 Caracteres y Cadenas frente a las Computadoras

I18N
Ahora, el alfabeto latino no es suficiente para toda la humanidad. Los usuarios de ese
alfabeto son minoría. Era necesario idear algo más flexible y capaz que ASCII, algo capaz
de hacer que todo el software del mundo sea susceptible de internacionalización, porque
diferentes idiomas usan alfabetos completamente diferentes, y a veces estos alfabetos no
son tan simples como el latino.

La palabra internacionalización se acorta comúnmente a I18N.

¿Por qué? Observa con cuidado, hay una I al inicio de la palabra, a continuación
hay 18 letras diferentes, y una N al final.

A pesar del origen ligeramente humorístico, el término se utiliza oficialmente en muchos


documentos y normas.

El software I18N es un estándar en los tiempos actuales. Cada programa tiene que ser
escrito de una manera que permita su uso en todo el mundo, entre diferentes culturas,
idiomas y alfabetos.

El código ASCII emplea ocho bits para cada signo. Ocho bits significan 256 caracteres
diferentes. Los primeros 128 se usan para el alfabeto latino estándar (tanto en
mayúsculas como en minúsculas). ¿Es posible colocar todos los otros caracteres utilizados
en todo el mundo a los 128 lugares restantes?

No, no lo es.
Puntos de código y páginas de códigos
Necesitamos un nuevo término: un punto de código.

Un punto de código es un numero que compone un caracter. Por ejemplo, el 32 es un


punto de código que compone un espacio en codificación ASCII. Podemos decir que el
código ASCII estándar consta de 128 puntos de código.

Como el ASCII estándar ocupa 128 de 256 puntos de código posibles, solo puedes hacer
uso de los 128 restantes.

No es suficiente para todos los idiomas posibles, pero puede ser suficiente para un
idioma o para un pequeño grupo de idiomas similares.

¿Se puede establecer la mitad superior de los puntos de código de manera diferente para
diferentes idiomas? Si, por supuesto. A tal concepto se le denomina una página de
códigos.

Una página de códigos es un estándar para usar los 128 puntos de código superiores
para almacenar caracteres específicos. Por ejemplo, hay diferentes páginas de códigos
para Europa Occidental y Europa del Este, alfabetos cirílicos y griegos, idiomas árabe y
hebreo, etc.

Esto significa que el mismo punto de código puede formar diferentes caracteres cuando
se usa en diferentes páginas de códigos.

Por ejemplo, el punto de código 200 forma una Č (una letra usada por algunas lenguas
eslavas) cuando lo utiliza la página de códigos ISO/IEC 8859-2, pero forma un Ш (una letra
cirílica) cuando es usado por la página de códigos ISO/IEC 8859-5.

En consecuencia, para determinar el significado de un punto de código específico, debes


conocer la página de códigos de destino.

En otras palabras, los puntos de código derivados del código de páginas son ambiguos.

2.1.1.3 Caracteres y Cadenas frente a las Computadoras

Unicode
Las páginas de códigos ayudaron a la industria informática a resolver problemas de I18N
durante algún tiempo, pero pronto resultó que no serían una solución permanente.

El concepto que resolvió el problema a largo plazo fue el Unicode.


Unicode asigna caracteres únicos (letras, guiones, ideogramas, etc.) a más de un millón
de puntos de código. Los primeros 128 puntos de código Unicode son idénticos a ASCII, y
los primeros 256 puntos de código Unicode son idénticos a la página de códigos ISO/IEC
8859-1 (una página de códigos diseñada para idiomas de Europa occidental).

UCS-4
El estándar Unicode no dice nada sobre como 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 proviene de Universal Character Set (Conjunto de Caracteres Universales).

UCS-4 emplea 32 bits (cuatro bytes) para almacenar cada carácter, y el código es solo el
número único de los puntos de código Unicode. Un archivo que contiene texto codificado
UCS-4 puede comenzar con un BOM (byte order mark - marca de orden de bytes), una
combinación no imprimible de bits que anuncia la naturaleza del contenido del archivo.
Algunas utilidades pueden requerirlo.

Como puedes ver, UCS-4 es un estándar bastante derrochador: aumenta el tamaño de un


texto cuatro veces en comparación con el estándar ASCII. Afortunadamente, hay formas
más inteligentes de codificar textos Unicode.

UTF-8
Uno de los más utilizados es UTF-8.

El nombre se deriva de Unicode Transformation Format (Formato de Transformación


Unicode).

El concepto es muy inteligente. UTF-8 emplea tantos bits para cada uno de los puntos de
código como realmente necesita para representarlos.
Por ejemplo:

• Todos los caracteres latinos (y todos los caracteres ASCII estándar) ocupan ocho
bits.
• Los caracteres no latinos ocupan 16 bits.
• Los ideógrafos CJK (China-Japón-Corea) ocupan 24 bits.

Debido a las características del método utilizado por UTF-8 para almacenar los puntos de
código, no es necesario usar el BOM, pero algunas de las herramientas lo buscan al leer
el archivo, y muchos editores lo configuran durante el guardado.

Python 3 es totalmente compatible con Unicode y UTF-8:

• Puedes usar caracteres codificados Unicode / UTF-8 para nombrar variables y


otras entidades.
• Puedes usarlos durante todas las entradas y salidas.

Esto significa que Python3 está completamente Internacionalizado.

2.1.1.4 RESUMEN DE SECCIÓN

Puntos Clave

1. Las computadoras almacenan caracteres como números. Hay más de una forma
posible de codificar caracteres, pero solo algunas de ellas ganaron popularidad en todo el
mundo y se usan comúnmente en TI: estas son ASCII (se emplea principalmente para
codificar el alfabeto latino y algunos de sus derivados) y UNICODE (capaz de codificar
prácticamente todos los alfabetos que utilizan los seres humanos).

2. Un número correspondiente a un carácter en particular se llama punto de código.


3. UNICODE utiliza diferentes formas de codificación cuando se trata de almacenar los
caracteres usando archivos o memoria de computadora: dos de ellas son UCS-4 y UTF-
8 (esta última es la más común ya que desperdicia menos espacio de memoria).

jercico 1

¿Qué es BOM?

Revisar

BOM (Byte Order Mark), Una Marca de Orden de Bytes es una combinación especial de
bits que anuncia la codificación utilizada por el contenido de un archivo (por ejemplo,
UCS-4 o UTF-B).

Ejercico 2

¿Está Python 3 internacionalizado?

Revisar

Sí, está completamente internacionalizado: podemos usar caracteres UNICODE dentro de


nuestro código, leerlos desde la entrada y enviarlos a la salida.

2.2.1.1 La naturaleza de las cadenas en Python

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.

Analicemos el código en el editor para entender de lo qué estamos hablando:

• Observa el Ejemplo 1. La función len() empleada en las cadenas devuelve el


número de caracteres que contiene el argumento. La salida del código es 2 .

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 carácter 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 verifícalo.


# Ejemplo 1

word = 'by'
print(len(word))

# Ejemplo 2

empty = ''
print(len(empty))

# Ejemplo 3

i_am = 'I\'m'
print(len(i_am))

2.2.1.2 La naturaleza de las cadenas en Python

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 siguiente es erróneo:

multiline = 'Línea #1
Línea #2'

print(len(multiline))

Afortunadamente, para este tipo de cadenas, Python ofrece una sintaxis simple,
conveniente y separada.

Observa el código en el editor. Así es como 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.

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.
Línea #1 contiene ocho caracteres. Las dos líneas juntas contienen 16 caracteres.
¿Perdimos un carácter? ¿Dónde? ¿Cómo?

No, no lo hicimos.

El carácter 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 carácter 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:

multiline = """Línea #1

Línea #2"""

print(len(multiline))

Elige el método que sea más cómodo. Ambos funcionan igual.

multiline = '''Línea #1
Línea #2'''

print(len(multiline))

2.2.1.3 La naturaleza de las cadenas en Python

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:

ab
ba
aaaaa
bbbb
salida

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)

2.2.1.4 La naturaleza de las cadenas en Python

Operaciones con cadenas: ord()


Si deseas saber el valor del punto de código ASCII/UNICODE de un carácter específico,
puedes usar la función ord() (proveniente de ordinal).

La función necesita una cadena de un carácter 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
salida
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.

# # Demostración de la función ord ().

char_1 = 'a'
char_2 = ' ' # space

print(ord(char_1))
print(ord(char_2))

2.2.1.5 La naturaleza de las cadenas en Python

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
α
salida

Nota:

• chr(ord(x)) == x
• ord(chr(x)) == x

De nuevo, realiza tus propios experimentos.

# Demostración de la función chr.


print(chr(97))
print(chr(945))

2.2.1.6 La naturaleza de las cadenas en Python


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. Ejecuta el programa:

# Indexando cadenas.

the_string = 'silly walks'

for ix in range(len(the_string)):
print(the_string[ix], end=' ')

print()

Ten cuidado, no intentes pasar los límites de la cadena, ya que provocará una excepción.

La salida del ejemplo es:

s i l l y w a l k s
salida

Por cierto, los índices negativos también se comportan como se espera. Comprueba esto
tú mismo.

Cadenas como secuencias: iterando


El iterar a través de las cadenas funciona también. Observa el siguiente ejemplo:

# Iterando a través de una cadena.

the_string = 'silly walks'

for character in the_string:


print(character, end=' ')

print()

La salida es la misma que el ejemplo anterior. Revísalo.


2.2.1.7 La naturaleza de las cadenas en Python

Rebanadas
Todo lo que sabes sobre rebanadas es utilizable.

Hemos reunido algunos ejemplos que muestran cómo funcionan las rebanadas en el
mundo de las cadenas. Observa el código en el editor, analízalo 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
efg
abd
e
e
adf
beg
salida

Ahora haz tus propios experimentos.

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

2.2.1.8 La naturaleza de las cadenas en Python

Los operadores in y not in


El operador 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 a continuación. Así es como el operador in funciona:

alphabet = "abcdefghijklmnopqrstuvwxyz"

print("f" in alphabet)
print("F" in alphabet)
print("1" in alphabet)
print("ghi" in alphabet)
print("Xyz" in alphabet)

La salida del ejemplo es:


True
False
False
True
False

salida

El operador not in

Como probablemente puedas deducir, el operador not in también es aplicable aquí.

Así es como funciona:

alphabet = "abcdefghijklmnopqrstuvwxyz"

print("f" not in alphabet)


print("F" not in alphabet)
print("1" not in alphabet)
print("ghi" not in alphabet)
print("Xyz" not in alphabet)

La salida del ejemplo es:

False
True
True
False
True
salida
2.2.1.9 La naturaleza de las cadenas en Python

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

alphabet = "abcdefghijklmnopqrstuvwxyz"
del alphabet[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:

alphabet = "abcdefghijklmnopqrstuvwxyz"
alphabet.append("A")

Con la ausencia del método append() , el método insert() también es ilegal:

alphabet = "abcdefghijklmnopqrstuvwxyz"
alphabet.insert(0, "A")

alphabet = "abcdefghijklmnopqrstuvwxyz"

# Escribe el código de prueba aquí.

2.2.1.10 La naturaleza de las cadenas en Python

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
salida

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 lo absoluto.

alphabet = "bcdefghijklmnopqrstuvwxy"

alphabet = "a" + alphabet


alphabet = alphabet + "z"

print(alphabet)

2.2.1.11 La naturaleza de las cadenas en Python

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
salida

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
salida

Nota: hemos utilizado corchetes para evitar que el espacio se pase por alto en tu pantalla.

# Demonstrando min() - Ejemplo 1:


print(min("aAbByYzZ"))

# Demonstrando min() - Ejemplo 2 y 3:


t = 'Los Caballeros Que Dicen "¡Ni!"'
print('[' + min(t) + ']')

t = [0, 1, 2]
print(min(t))

2.2.1.12 La naturaleza de las cadenas en Python

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
salida

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
salida

Realiza tus propios experimentos.


# Demostración de max() - Ejemplo 1:
print(max("aAbByYzZ"))

# Demostración de max() - Ejemplo 2 & 3:


t = 'Los Caballeros Que Dicen "¡Ni!"'
print('[' + max(t) + ']')

t = [0, 1, 2]
print(max(t))

2.2.1.13 La naturaleza de las cadenas en Python

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

Por lo tanto, el ejemplo en la salida del editor es:

2
7
1
salida
# Demonstrando el método index():
print("aAbByYzZaA".index("b"))
print("aAbByYzZaA".index("Z"))
print("aAbByYzZaA".index("A")

2.2.1.14 La naturaleza de las cadenas en Python

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']


salida

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?

Es:

2
0
salida

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.

# Demostración de la función list():


print(list("abcabc"))

# Demostración de la función list():


print("abcabc".count("b"))
print('abcabc'.count("d"))

2.2.1.15 RESUMEN DE SECCIÓN

Puntos Claves
1. Las cadenas de Python son secuencias inmutables y se pueden indexar, dividir en
rebanadas e iterar como cualquier otra secuencia, además de estar sujetas a los
operadores in y not in . Existen dos tipos de cadenas en Python:

• Cadenas de una línea, las cuales no pueden cruzar los límites de una línea, las
denotamos usando apóstrofes ( 'cadena' ) o comillas ( "cadena" ).
• Cadenas multilínea, que ocupan más de una línea de código fuente, delimitadas
por apóstrofes triples:
'''
cadena
'''

"""
cadena
"""

2. La longitud de una cadena está determinada por la función len() . El carácter de


escape ( \ ) no es contado. Por ejemplo:

print(len("\n\n"))

Su salida es 2 .

3. Las cadenas pueden ser concatenadas usando el operador + , y replicadas usando el


operador * . Por ejemplo:

asterisk = '*'
plus = "+"
decoration = (asterisk + plus) * 4 + asterisk
print(decoration)

salida *+*+*+*+* .

4. El par de funciones chr() y ord() se pueden utilizar para crear un carácter utilizando
su punto de código y para determinar un punto de código correspondiente a un carácter.
Las dos expresiones siguientes son siempre verdaderas:

chr(ord(character)) == character
ord(chr(codepoint)) == codepoint

5. Algunas otras funciones que se pueden aplicar a cadenas son:

• list() – crea una lista que consta de todos los caracteres de la cadena.
• max() – encuentra el carácter con el punto de código máximo.
• min() – encuentra el carácter con el punto de código mínimo.

6. El método llamado index() encuentra el índice de una subcadena dada dentro de la


cadena.

Ejercicio 1

¿Cuál es la longitud de la siguiente cadena asumiendo que no hay espacios en blanco


entre las comillas?

"""
"""
Revisar

Ejercicio 2

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

s = 'yesteryears'
the_list = list(s)
print(the_list[3:6])

Revisar

['t', 'e', 'r']

Ejercicio 3

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

for ch in "abc":
print(chr(ord(ch) + 1), end='')

Revisar

bcd

2.3.1.1 Métodos de cadenas

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 carácter 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 carácter visible), se convertirá
a mayúsculas.
• Todas las letras restantes de la cadena se convertirán a minúsculas.

No olvides que:
• La cadena original desde la cual se invoca el método no se cambia de ninguna
manera, la inmutabilidad de una cadena debe obedecerse sin reservas.
• La cadena modificada (en mayúscula en este caso) se devuelve como resultado; si
no se usa de alguna manera (asígnala a una variable o pásala a una función /
método) desaparecerá sin dejar rastro.

Nota: los métodos no tienen que invocarse solo dentro de las variables. Se pueden
invocar directamente desde dentro de literales de cadena. Usaremos esa convención
regularmente: simplificará los ejemplos, ya que los aspectos más importantes no
desaparecerán entre asignaciones innecesarias.

Echa un vistazo al ejemplo en el editor. Ejecútalo.

Esto es lo que imprime:

Abcd

salida

Prueba algunos ejemplos más avanzados y verifíca su salida:

print("Alpha".capitalize())
print('ALPHA'.capitalize())
print(' Alpha'.capitalize())
print('123'.capitalize())
print("αβγδ".capitalize())

# Demostración del método capitalize():


print('aBcD'.capitalize())

2.3.1.2 Métodos de cadenas

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 donde comienza y termina
realmente la cadena centrada.

Su salida se ve de la siguiente manera:


[ alpha ]
salida

Si la longitud del campo de destino es demasiado pequeña para ajustarse a la cadena, se


devuelve la cadena original.

Puedes ver el método center() en más ejemplos aquí:

print('[' + 'Beta'.center(2) + ']')


print('[' + 'Beta'.center(4) + ']')
print('[' + 'Beta'.center(6) + ']')

Ejecuta el código anterior y verifica que salidas produce.

La variante de dos parámetros de center() hace uso del carácter 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********]
salida

Realiza más experimentos.

# Demostración del método center():


print('[' + 'alpha'.center(10) + ']')

2.3.1.3 Métodos de cadenas

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, analízalo y ejecútalo. Su salida es:

si
salida

Ahora deberías poder predecir la salida del fragmento de código a continuación:


t = "zeta"
print(t.endswith("a"))
print(t.endswith("A"))
print(t.endswith("et"))
print(t.endswith("eta"))

Ejecuta el código para verificar tus predicciones.

# Demostración del método endswith():


if "epsilon".endswith("on"):
print("si")
else:
print("no")

2.3.1.4 Métodos de cadenas

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
salida

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 = 'theta'
print(t.find('eta'))
print(t.find('et'))
print(t.find('the'))
print(t.find('ha'))

¿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 (no tiene que
estar dentro de la cadena).

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

the_text = """A variation of the ordinary lorem ipsum


text has been used in typesetting since the 1960s
or earlier, when it was popularized by advertisements
for Letraset transfer sheets. It was introduced to
the Information Age in the mid-1980s by the Aldus Corporation,
which employed it in graphics and word-processing templates
for its desktop publishing program PageMaker (from Wikipedia)"""

fnd = the_text.find('the')
while fnd != -1:
print(fnd)
fnd = the_text.find('the', fnd + 1)

El código imprime los índices de todas las ocurrencias del artículo the, y su salida se ve
así:

15
80
198
221
238
salida

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
estar dentro de la cadena).

Por lo tanto, las salidas de ejemplo son:

1
-1
salida

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

2.3.1.5 Métodos de cadenas

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
True
False
False
False
salida

Existen tres ejemplos más aquí:

t = 'Six lambdas'
print(t.isalnum())

t = 'ΑβΓδ'
print(t.isalnum())

t = '20E1'
print(t.isalnum())

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

2.3.1.6 Métodos de cadenas

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
salida

El método isdigit()
Al contrario, el método isdigit() busca solo dígitos - cualquier otra cosa
produce False(falso) como resultado.

Observa el Ejemplo 2, su salida es:

True
False
salida

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("Year2019".isdigit())
2.3.1.7 Métodos de cadenas

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
salida

El método isspace()
El método isspace() identifica espacios en blanco solamente - no tiene en cuenta
ningún otro carácter (el resultado es entonces False ).

Observa el Ejemplo 2 en el editor, genera:

True
True
False
salida

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
salida
# 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())
2.3.1.8 Métodos de cadenas

El método join()
El método join() es algo complicado, así que déjanos guiarte paso a paso:

• Como su nombre lo indica, el método realiza una unión y espera un argumento


del tipo lista; se debe asegurar que todos los elementos de la lista sean cadenas:
de lo contrario, el método generará una excepción TypeError.
• Todos los elementos de la lista serán unidos en una sola cadena pero...
• ... la cadena desde la que se ha invocado el método será utilizada como
separador, puesta entre las cadenas.
• La cadena recién creada se devuelve como resultado.

Echa un vistazo al ejemplo en el editor. Vamos a analizarlo:

• 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
salida

# Demonstrating the join() method:


print(",".join(["omicron", "pi", "rho"]))

2.3.1.9 Métodos de cadenas

El método lower()
El método lower() genera una copia de una cadena, reemplaza todas las letras
mayúsculas con sus equivalentes en minúsculas, y devuelve la cadena como resultado.
Nuevamente, la cadena original permanece intacta.

Si la cadena no contiene caracteres en mayúscula, el método devuelve la cadena original.

Nota: El método lower() no toma ningún parámetro.

La salida del ejemplo del editor es:

sigma=60
salida
Como ya sabes, realiza tus propios experimentos.

# Demostración del método lower():


print("SiGmA=60".lower())

2.3.1.10 Métodos de cadenas

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 ]
salida

El método con un parámetro lstrip() hace lo mismo que su versión sin parámetros,
pero elimina todos los caracteres incluidos en el argumento (una cadena), no solo
espacios en blanco:

print("www.cisco.com".lstrip("w."))

Aquí no se necesitan corchetes, ya que el resultado es el siguiente:

cisco.com
salida

¿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() + "]")
2.3.1.11 Métodos de cadenas

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.

La salida del ejemplo es:

www.pythoninstitute.org
Thare are it!
Apple
salida

Si el segundo argumento es una cadena vacía, reemplazar significa realmente eliminar el


primer argumento de la cadena. ¿Qué tipo de magia ocurre si el primer argumento es
una cadena vacía?

La variante del método 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", ""))

2.3.1.12 Métodos de cadenas

El método rfind()
Los métodos de uno, dos y tres parámetros denominados rfind() hacen casi lo mismo
que sus contrapartes (las que carecen del prefijo r), pero comienzan sus búsquedas
desde el final de la cadena, no el principio (de ahí el prefijo r, de reversa).

Echa un vistazo al código en el editor e intenta predecir su salida. Ejecuta el código para
verificar si tenías razón.
# Demostración del método rfind():
print("tau tau tau".rfind("ta"))
print("tau tau tau".rfind("ta", 9))
print("tau tau tau".rfind("ta", 3, 9))

2.3.1.13 Métodos de cadenas

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.

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

2.3.1.14 Métodos de cadenas

El método split()
El método split() divide la cadena y crea una lista de todas las subcadenas detectadas.

El método asume que las subcadenas están delimitadas por espacios en blanco - los
espacios no participan en la operación y no se copian en la lista resultante.

Si la cadena está vacía, la lista resultante también está vacía.

Observa el código en el editor. El ejemplo produce el siguiente resultado:

['phi', 'chi', 'psi']


salida

Nota: la operación inversa se puede realizar por el método join() .

# Demostración del método split():


print("phi chi\npsi".split())
2.3.1.15 Métodos de cadenas

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.

Observa el ejemplo en el editor. Este es el resultado:

False
True
salida

El método strip()
El método strip() combina los efectos causados por rstrip() y lstrip() - crea una
nueva cadena que carece de todos los espacios en blanco iniciales y finales.

Observa el segundo ejemplo en el editor. Este es el resultado que devuelve:

[aleph]
salida

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() + "]")

2.3.1.16 Métodos de cadenas

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? No se verá nada bien,
pero debes observarla:
yO SÉ QUE NO SÉ NADA.
salida

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.

Observa el segundo ejemplo en el editor. ¿Puedes adivinar su salida? Este es el resultado:

Yo Sé Que No Sé Nada. Parte 1.


salida

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.

Observa el tercer ejemplo en el editor. Produce:

YO SÉ QUE NO SÉ NADA. PARTE 2.


salida

¡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 que cosas podemos hacer
con las cadenas.

# Demostración del método swapcase():


print("Yo sé que no sé nada.".swapcase())

print()

# Demostración del método title():


print("Yo sé que no sé nada. Part 1.".title())

print()

# Demostración del método upper():


print("Yo sé que no sé nada. Part 2.".upper())

2.3.1.17 RESUMEN DE SECCIÓN

Puntos Clave
1. Algunos de los métodos que ofrecen las cadenas son:

• capitalize() : cambia todas las letras de la cadena a mayúsculas.


• center() : centra la cadena dentro de una longitud conocida.
• count() : cuenta las ocurrencias de un carácter dado.
• join() : une todos los elementos de una tupla/lista en una cadena.
• lower() : convierte todas las letras de la cadena en minúsculas.
• lstrip() : elimina los caracteres en blanco al principio de la cadena.
• replace() : reemplaza una subcadena dada con otra.
• rfind() : encuentra una subcadena comenzando por el final de la cadena.
• rstrip() : elimina los caracteres en blanco al final de la cadena.
• split() : divide la cadena en una subcadena usando un delimitador dado.
• strip() : elimina los espacios en blanco iniciales y finales.
• swapcase() : intercambia las mayúsculas y minúsculas de las letras.
• title() : hace que la primera letra de cada palabra sea mayúscula.
• upper() : convierte todas las letras de la cadena en letras mayúsculas.

2. El contenido de las cadenas se puede determinar mediante los siguientes métodos


(todos devuelven valores booleanos):

• endswith() : ¿La cadena termina con una subcadena determinada?


• isalnum() : ¿La cadena consta solo de letras y dígitos?
• isalpha() : ¿La cadena consta solo de letras?
• islower() : ¿La cadena consta solo de letras minúsculas?
• isspace() : ¿La cadena consta solo de espacios en blanco?
• isupper() : ¿La cadena consta solo de letras mayúsculas?
• startswith() : ¿La cadena consta solo de letras mayúsculas?

Ejercicio 1

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

for ch in "abc123XYX":
if ch.isupper():
print(ch.lower(), end='')
elif ch.islower():
print(ch.upper(), end='')
else:
print(ch, end='')

Revisar

ABC123xyz
Ejercicio 2

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

s1 = '¿Dónde están las nevadas de antaño?'


s2 = s1.split()
print(s2[-2])

Revisar

de

Ejercicio 3

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

the_list = ['¿Dónde', 'están', 'las', 'nevadas?']


s = '*'.join(the_list)
print(s)

Revisar

¿Dónde*están*las*nevadas?

Ejercicio 4

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

s = 'Es fácil o imposible'


s = s.replace('fácil', 'difícil').replace('im', '')
print(s)

Revisar

Es difícil o posible

2.3.1.18 Tu propio split

LABORATORIO
Tiempo Estimado
20-25 minutos
Nivel de dificultad
Medio

Objetivos
• Mejorar las habilidades del alumno al trabajar con cadenas.
• Utilizar los métodos incorporados de Python para las cadenas.

Escenario
Ya sabes como funiona el método split() . Ahora queremos que lo pruebes.

Tu tarea es escribir tu propia función, que se comporte casi como el método


original split() , por ejemplo:

• Debe aceptar únicamente un argumento: una cadena.


• Debe devolver una lista de palabras creadas a partir de la cadena, dividida en los
lugares donde la cadena contiene espacios en blanco.
• Si la cadena está vacía, la función debería devolver una lista vacía.
• Su nombre debe ser mysplit() .

Utiliza 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 mysplit(strng):
#
# coloca tu código aquí
#

print(mysplit("Ser o no ser, esa es la pregunta"))


print(mysplit("Ser o no ser,esa es la pregunta"))
print(mysplit(" "))
print(mysplit(" abc "))
print(mysplit(""))
2.4.1.1 Cadenas en acción

Comparando cadenas
Las cadenas en Python pueden ser comparadas usando el mismo conjunto de
operadores que se emplean con los números.

Observa estos operadores: también sirven para 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 serlo de ninguna
manera) de problemas lingüísticos sutiles, simplemente compara valores de puntos de
código, carácter por carácter.

Los resultados que se 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 de los mismos caracteres en el mismo orden.
Del mismo modo, dos cadenas no son iguales cuando no consisten de los mismos
caracteres en el mismo orden.

Ambas comparaciones dan True (verdadero) como resultado:

'alpha' == 'alpha'
'alpha' != 'Alpha'

La relación entre cadenas se determina al comparar el primer carácter 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í:

'alpha' < 'alphabet'

La comparació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'

# Probar ejemplos aquí.

2.4.1.2 Cadenas en acción

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.

Observa los ejemplos:

'10' == '010'
'10' > '010'
'10' > '8'
'20' < '8'
'20' < '80'

Producen los siguientes resultados:

False
True
False
True
True
salida

El comparar cadenas con los 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 (falso), mientras que el
segundo siempre devuelve True (verdadero).

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
salida

Ejecuta todos los ejemplos y realiza más experimentos.

# Probar ejemplos aquí.

2.4.1.3 Cadenas en acción

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', 'alpha', 'pi', 'gamma']

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

Observa el código en el editor y ejecútalo. El código produce el siguiente resultado:

['omega', 'alpha', 'pi', 'gamma']


['alpha', 'gamma', 'omega', 'pi']
salida

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() .
La salida no ha cambiado:

['omega', 'alpha', 'pi', 'gamma']


['alpha', 'gamma', 'omega', 'pi']
salida

Si necesitas un ordenamiento diferente, debes convencer a la función o método de


cambiar su comportamiento predeterminado. Lo discutiremos pronto.

# Demostración de la función sorted():


first_greek = ['omega', 'alpha', 'pi', 'gamma']
first_greek_2 = sorted(first_greek)

print(first_greek)
print(first_greek_2)

print()

# Demostración del método sort():


second_greek = ['omega', 'alpha', 'pi', 'gamma']
print(second_greek)

second_greek.sort()
print(second_greek)

2.4.1.4 Cadenas en acción

Cadenas frente a números


Hay dos cuestiones adicionales que deberían discutirse aquí: ¿Cómo convertir un número
(un entero o un flotante) en una cadena, y viceversa? Puede ser necesario realizar tal
transformación. Además, es una forma rutinaria de procesar datos de entrada o salida.

La conversión de cadena a número es simple, ya que siempre es posible. Se realiza


mediante una función llamada str() .

Justo como aquí:

itg = 13
flt = 1.3
si = str(itg)
sf = str(flt)

print(si + ' ' + sf)


La salida del código es:

13 1.3
salida

La transformación inversa solo es posible cuando la cadena representa un número


válido. Si no se cumple la condición, espera una excepción ValueError.

Emplea la función int() si deseas obtener un entero, y float() si necesitas un valor


punto flotante.

Justo como aquí:

si = '13'
sf = '1.3'
itg = int(si)
flt = float(sf)

print(itg + flt)

Esto es lo que verás en la consola:

14.3
salida

En la siguiente sección, te mostraremos algunos programas simples que procesan


cadenas.

# Probar ejemplos aquí.

2.4.1.5 RESUMEN DE SECCIÓN

Puntos Claves
1. Las cadenas se pueden comparar con otras cadenas utilizando operadores de
comparación generales, pero compararlas con números no da un resultado razonable,
porque ninguna cadena puede ser igual a ningún otro número. Por ejemplo:

• cadena == número es siempre False (falso).


• cadena != número es siempre True (verdadero).
• cadena >= número siempre genera una excepción.

2. El ordenamiento de listas de cadenas se puede realizar mediante:

• Una función llamada sorted() , crea una nueva, lista ordenada.


• Un método llamado sort() , el cual ordena la lista en el momento.
3. Un número se puede convertir en una cadena empleando la función str() .

4. Una cadena se puede convertir en un número (aunque no todas las cadenas)


empleando ya sea la función int() o float() . La conversión falla si la cadena no
contiene un número válido (se genera una excepción en dicho caso).

Ejercicio 1

¿Cuál de las siguientes líneas describe una condición verdadera?

Revisar

1, 3 y 4

Ejercicio 2

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

s1 = '¿Dónde están las nevadas de antaño?'


s2 = s1.split()
s3 = sorted(s2)
print(s3[1])

Revisar

de

Ejercicio 3

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

s1 = '12.8'
i = int(s1)
s2 = str(i)
f = float(s2)
print(s1 == s2)

Revisar

El código genera una excepción ValueError

2.4.1.6 LABORATORIO: Un Display LED

LABORATORIO
Tiempo Estimado
30 minutos

Nivel de dificultad
Medio

Objetivos
• Mejorar las habilidades del alumno al 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: https://en.wikipedia.org/wiki/Seven-
segment_display.

Tu tarea es escribir un programa que puede simular el funcionamiento de un display de


siete segmentos, aunque vas a usar LEDs individuales en lugar de segmentos.

Cada dígito es construido con 13 LEDs (algunos iluminados, otros apagados, por
supuesto), así es como lo imaginamos:

# ### ### # # ### ### ### ### ### ###


# # # # # # # # # # # # # #
# ### ### ### ### ### # ### ### # #
# # # # # # # # # # # # #
# ### ### # ### ### # ### ### ###

Nota: el número 8 muestra todas las luces LED encendidas.

Tu código debe mostrar cualquier número entero no negativo ingresado por el usuario.

Consejo: puede ser muy útil usar una lista que contenga patrones de los diez dígitos.

Datos de prueba
Entrada de muestra:

123
Salida de muestra:

# ### ###
# # #
# ### ###
# # #
# ### ###

Entrada de muestra:

9081726354

Salida de muestra:

### ### ### # ### ### ### ### ### # #


# # # # # # # # # # # # # #
### # # ### # # ### ### ### ### ###
# # # # # # # # # # # # #
### ### ### # # ### ### ### ### #

2.5.1.1 Cuatro programas simples

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 la 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 carácter actual no es alfabético...
• La línea 06: ...ignoralo.
• La línea 07: convierta la letra a mayúsculas (es preferible hacerlo a ciegas, en lugar
de verificar si es necesario o no).
• La línea 08: obtén el código de la letra e increméntalo en uno.
• La línea 09: si el código resultante ha "dejado" el alfabeto latino (si es mayor que el
código de la Z)...
• La línea 10: ... cámbialo al código de la A.
• La línea 11: agrega el carácter recibido al final del mensaje cifrado.
• La línea 13: imprime el cifrado.

El código, con este mensaje:

AVE CAESAR

Da como salida:

BWFDBFTBS
salida

Haz tus propias pruebas.

# Cifrado César.
text = input("Ingresa tu mensaje: ")
cipher = ''
for char in text:
if not char.isalpha():
continue
char = char.upper()
code = ord(char) + 1
if code > ord('Z'):
code = ord('A')
cipher += chr(code)

print(cipher)

2.5.1.2 Cuatro programas simples

El Cifrado César: descifrando un mensaje


La operación inversa ahora debería ser clara para ti: solo presentamos el código tal como
está, sin ninguna explicación.
Observa el código en el editor. Comprueba cuidadosamente si funciona. Usa el
criptograma del programa anterior.

# Cifrado César - descifrar un mensaje.


cipher = input('Ingresa tu criptograma: ')
text = ''
for char in cipher:
if not char.isalpha():
continue
char = char.upper()
code = ord(char) - 1
if code < ord('A'):
code = ord('Z')
text += chr(code)

print(text)

2.5.1.3 Cuatro programas simples

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.
• 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
de la falla.

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.

line = input("Ingresa una línea de números, sepáralos con


espacios: ")
strings = line.split()
total = 0
try:
for substr in strings:
total += float(substr)
print("El total es:", total)
except:
print(substr, "no es un numero.")

2.5.1.4 Cuatro programas simples

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 válido.
• El número de cuenta real (hasta 30 caracteres alfanuméricos; la longitud de esa
parte depende del país).

El estándar dice que la validación requiere los siguientes pasos (según Wikipedia):
• (Paso 1) Verificar que la longitud total del IBAN sea correcta según el país (este
programa no lo hará, pero puedes modificar el código para cumplir con este
requisito si lo deseas; nota: pero debes enseñar al código a conocer todas las
longitudes utilizadas en Europa).
• (Paso 2) Mueve los cuatro caracteres iniciales al final de la cadena (es decir, el
código del país y los dígitos de verificación).
• (Paso 3) Reemplaza cada letra en la cadena con dos dígitos, expandiendo así la
cadena, donde A = 10, B = 11 ... Z = 35.
• (Paso 4) Interpreta la cadena como un entero decimal y calcula el residuo de ese
número dividiéndolo 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.
• Línea 15: si el carácter es un dígito...
• 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 un IBAN, por favor: ")


iban = iban.replace(' ','')

if not iban.isalnum():
print("Has introducido caracteres no válidos.")
elif len(iban) < 15:
print("El IBAN ingresado es demasiado corto.")
elif len(iban) > 31:
print("El IBAN ingresado es demasiado largo.")
else:
iban = (iban[4:] + iban[0:4]).upper()
iban2 = ''
for ch in iban:
if ch.isdigit():
iban2 += ch
else:
iban2 += str(10 + ord(ch) - ord('A'))
iban = int(iban2)
if iban % 97 == 1:
print("El IBAN ingresado es válido.")
else:
print("El IBAN ingresado no es válido.")

2.5.1.5 RESUMEN DE SECCIÓN

Puntos Claves

1. Las cadenas son herramientas clave en el procesamiento de datos modernos, ya que la


mayoría de los datos útiles son en realidad cadenas. Por ejemplo, el uso de un motor de
búsqueda web (que parece bastante trivial en estos días) utiliza un procesamiento de
cadenas extremadamente complejo, que involucra cantidades inimaginables de datos.

2. El comparar cadenas de forma estricta (como lo hace Python) puede ser muy
insatisfactorio cuando se trata de búsquedas avanzadas (por ejemplo, durante consultas
extensas a bases de datos). En respuesta a esta demanda, se han creado e implementado
una serie de algoritmos de comparación de cadenas difusos. Estos algoritmos pueden
encontrar cadenas que no son iguales en el sentido de Python, pero que son similares.

Uno de esos conceptos es la Distancia Hamming, que se utiliza para determinar la


similitud de dos cadenas. Si este tema te interesa, puedes encontrar más información al
respecto aquí: https://en.wikipedia.org/wiki/Hamming_distance. Otra solución del mismo
tipo, pero basada en un supuesto diferente, es la Distancia Levenshtein descrita
aquí: https://en.wikipedia.org/wiki/Levenshtein_distance.

3. Otra forma de comparar cadenas es encontrar su similitud acústica, lo que significa un


proceso que lleva a determinar si dos cadenas suenan similares (como "echo" y "hecho").
Esa similitud debe establecerse para cada idioma (o incluso dialecto) por separado.

Un algoritmo utilizado para realizar una comparación de este tipo para el idioma Inglés se
llama Soundex y se inventó, no lo creerás, en 1918. Puedes encontrar más información al
respecto aquí: https://en.wikipedia.org/wiki/Soundex.

4. Debido a la precisión limitada de los datos enteros y flotantes nativos, a veces es


razonable almacenar y procesar valores numéricos enormes como cadenas. Esta es la
técnica que usa Python cuando se le fuerza a operar con un número entero que consta
de una gran cantidad de dígitos.

2.6.1.1 Errores: el pan diario del programador

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

x = float(input("Ingresa x: "))

ValueError: could not convert string to float: 'Abracadabra'


salida

O algo como esto:


Ingresa x: -1

Traceback (most recent call last):

File "sqrt.py", line 4, in <module>

y = math.sqrt(x)

ValueError: math domain error


salida

¿Puedes protegerte de tales sorpresas? Por supuesto. 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)

2.6.1.2 Errores: el pan diario del programador

Excepciones
Cada vez que tu código intenta hacer algo erróneo, irresponsable o inaplicable, Python
hace dos cosas:

• Detiene tu programa.
• Crea un tipo especial de dato, llamado excepción.

Ambas actividades llevan por nombre generar una excepción. Podemos decir que Python
siempre genera una excepción (o que una excepción ha sido generada) cuando no tiene
idea de qué hacer con el código.

¿Qué ocurre después?

• La excepción generada espera que alguien o algo lo note y haga algo al respecto.
• Si la excepción no es resuelta, el programa será terminado abruptamente, y verás
un mensaje de error enviado a la consola por Python.
• De otra manera, si se atiende la excepción y es manejada apropiadamente, el
programa puede reanudarse y su ejecución puede continuar.

Python proporciona herramientas efectivas que permiten observar, identificar y manejar


las excepciones eficientemente. Esto es posible debido a que todas las excepciones
potenciales tienen un nombre específico, por lo que se pueden clasificar y reaccionar a
ellas adecuadamente.
Ya conoces algunos nombres de las excepciones. Observa el siguiente mensaje de
diagnóstico:

ValueError: math domain error


salida

La palabra resaltada es el nombre de la excepción. Vamos a familiarizarnos con algunas


otras excepciones.

2.6.1.3 Errores: el pan diario del programador

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


File "div.py", line 2, in
value /= 0
ZeroDivisionError: division by zero
salida

Este error es llamado ZeroDivisionError.

value = 1
value /= 0

2.6.1.4 Errores: el pan diario del programador


Excepciones: continuación
Observa el código en el editor. ¿Qué pasará cuando lo ejecutes? Revísalo.

Verás el siguiente mensaje en respuesta:

Traceback (most recent call last):


File "lst.py", line 2, in
x = list[0]
IndexError: list index out of range
salida

Este es el IndexError (Error de Índice).

my_list = []
x = my_list[0]

2.6.1.5 Errores: el pan diario del programador

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 clave reservada.

La receta para el éxito es la siguiente:

• Primero, se debe intentar (try) hacer algo.


• Después, tienes que comprobar si todo salió bien.

Pero, ¿no sería mejor verificar primero todas las circunstancias y luego hacer algo solo si
es seguro?

Justo como el ejemplo en el editor.

Es cierto que esta forma puede parecer la mas natural y comprensible, pero en realidad,
este método no facilita la programación. Todas estas revisiones pueden hacer al código
demasiado grande e ilegible.

Python prefiere un enfoque completamente diferente.

first_number = int(input("Ingresa el primer numero: "))


second_number = int(input("Ingresa el segundo numero: "))

if second_number != 0:
print(first_number / second_number)
else:
print("Esta operación no puede ser realizada.")

print("FIN.")

2.6.1.6 Errores: el pan diario del programador | try-except

Excepciones: continuación
Observa el código en el editor. Este es el enfoque favorito de Python.

Nota:

• La palabra reservada try comienza con un bloque de código el cual puede o no


estar funcionando correctamente.
• Después, Python intenta realizar la acción arriesgada: si falla, se genera una
excepción y Python comienza a buscar una solución.
• La palabra reservada except comienza con un bloque de código que
será ejecutado si algo dentro del bloque try sale mal: si se genera una excepción
dentro del bloque anterior try , fallará aquí, entonces el código ubicado después
de la palabra clave reservada except debería proporcionar una reacción
adecuada a la excepción planteada.
• Por ultimo, se regresa al nivel de anidación anterior, es decir, se termina la
sección try-except.

Ejecuta 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 clave reservada except: , esto significa que algunas de las
instrucciones del bloque pueden ser silenciosamente omitidas.
first_number = int(input("Ingresa el primer numero: "))
second_number = int(input("Ingresa el segundo numero: "))

try:
print(first_number / second_number)
except:
print("Esta operación no puede ser realizada.")

print("FIN.")

2.6.1.7 Errores: el pan diario del programador | try-except

Excepciones: continuación
Observa el código en el editor. Te ayudará a comprender este mecanismo.

Esta es la salida que produce:

1
Oh cielos, algo salió mal...
3
salida

Nota: la instrucción print("2") se perdió en el proceso.

try:
print("1")
x = 1 / 0
print("2")
except:
print("Oh cielos, algo salió mal...")

print("3")

2.6.1.8 Errores: el pan diario del programador | try-except

Excepciones: continuación
Este enfoque tiene una desventaja importante: si existe la posibilidad de que más de una
excepción se salte a un apartado except: , puedes tener problemas para descubrir lo
que realmente sucedió.

Al igual que en el código en el editor. Ejecútalo y ve lo que 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 ve de la siguiente manera:

try:
:
except exc1:
:
except exc2:
:
except:
:

Así es como funciona:

• Si el try genera la excepción exc1 , esta será manejada por el bloque except
exc1: .
• De la misma manera, si el try genera la excepción exc2 , esta será manejada por
el bloque except exc2: .
• Si el try genera 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 salió mal...")

print("FIN.")

2.6.1.9 Errores: el pan diario del programador | try-except

Excepciones: continuación
Observa el código en el editor. Nuestra solución 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.
salida
• Si se ingresa 0 , dirá:

No puedes dividir entre cero, lo siento.


FIN.
salida

• Si se ingresa cualquier cadena no entera, verás:

Debes ingresar un valor entero.


FIN.
salida

• (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 salió mal...


FIN.
salida

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 salió mal...")

print("FIN.")

2.6.1.10 Errores: el pan diario del programador | try-


except

Excepciones: continuación
No olvides que:
• Los bloques except son analizados en el mismo orden en que aparecen en el
código.
• No debes usar más de un bloque de excepción con el mismo nombre.
• El número de diferentes bloques except es arbitrario, la única condición es que si
se emplea el try , debes poner al menos un except (nombrado o no) después de
el.
• La palabra clave 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 salió mal...


FIN.
salida

Inténtalo tú mismo. Ejecuta el programa.

try:
x = int(input("Ingresa un numero: "))
y = 1 / x
print(y)
except ValueError:
print("Debes ingresar un valor entero.")
except:
print("Oh cielos, algo salió mal...")

print("FIN.")
2.6.1.11 Errores: el pan diario del programador | try-
except

Excepciones: continuación
Echemos a perder el código una vez más.

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

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

salida

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.

try:
x = int(input("Ingresa un número: "))
y = 1 / x
print(y)
except ValueError:
print("Debes ingresar un valor entero.")

print("FIN.")

2.6.1.12 RESUMEN DE SECCIÓN

Puntos Clave

1. Una excepción es un evento durante la ejecución del programa causado por una
situación anormal. La excepción debe manejarse para evitar la terminación del programa.
La parte del código que se sospecha que es la fuente de la excepción debe colocarse
dentro del bloque try .

Cuando ocurre la excepción, la ejecución del código no se termina, sino que salta al
bloque except . Este es el lugar donde debe llevarse a cabo el manejo de la excepción. El
esquema general para tal construcción es el siguiente:
:
# El código que siempre corre suavemente.
:
try:
:
# Código arriesgado.
:
except:
:
# La gestión de la crisis se lleva a cabo aquí.
:
:
# De vuelta a la normalidad.
:

2. Si necesitas manejar más de una excepción proveniente del mismo bloque try ,
puedes agregar más de un bloque except , pero debes etiquetarlos con diferentes
nombres, así:

:
# El código que siempre corre suavemente.
:
try:
:
# Código arriesgado.
:
except Except_1:
# La gestión de la crisis se lleva a cabo aquí.
except Except_2:
# Salvamos el mundo aqui.
:
# De vuelta a la normalidad.
:

En el mejor caso, se ejecuta uno de los bloques except ; ninguno de los bloques se
ejecuta cuando la excepción generada no coincide con ninguna de las excepciones
especificadas.

3. No se puede agregar más de un bloque de excepción sin nombre después de los


bloques con nombre.

:
# El código que siempre corre suavemente.
:
try:
:
# Código arriesgado.
:
except Except_1:
# La gestión de la crisis se lleva a cabo aquí.
except Except_2:
# Salvamos el mundo aqui.
except:
# Todos los demás problemas caen aquí.
:
# De vuelta a la normalidad.
:

Ejercicio 1

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

try:
print("Tratemos de hacer esto")
print("#"[2])
print("¡Tuvimos éxito!")
except:
print("Hemos fallado")
print("Hemos terminado")

Revisar

Tratemos de hacer esto


Hemos fallado
Hemos terminado

Ejercicio 2

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

try:
print("alpha"[1/0])
except ZeroDivisionError:
print("cero")
except IndexingError:
print("índice")
except:
print("algo")

Revisar

cero
2.7.1.1 La anatomía de las excepciones

Excepciones
Python 3 define 63 excepciones integradas, y todas ellas forman una jerarquía en forma
de árbol, aunque el árbol es un poco extraño ya que su raíz se encuentra en la parte
superior.

Algunas de las excepciones integradas son más generales (incluyen otras excepciones)
mientras que otras son completamente concretas (solo se representan a sí mismas).
Podemos decir que cuanto más cerca de la raíz se encuentra una excepción, más general
(abstracta) es. A su vez, las excepciones ubicadas en los extremos del árbol (podemos
llamarlas hojas) son concretas.

Observa la figura:

Muestra una pequeña sección del árbol completo de excepciones. Comencemos


examinando el árbol desde la hoja ZeroDivisionError.

Nota:

• ZeroDivisionError es un caso especial de una clase de excepción más general


llamada ArithmeticError.
• ArithmeticError es un caso especial de una clase de excepción más general
llamada solo Exception.
• Exception es un caso especial de una clase más general
llamada BaseException.

Podemos describirlo de la siguiente manera (observa la dirección de las flechas; siempre


apuntan a la entidad más general):

BaseException

Exception

ArithmeticError

ZeroDivisionError

Te mostraremos el funcionamiento esta generalización. Comencemos con un código


realmente simple.

2.7.1.2 La anatomía de las excepciones

Excepciones: continuación
Observa el código en el editor. Es un ejemplo simple para comenzar. Ejecútalo.

La salida que esperamos ver es la siguiente:

Uuuppsss...
FIN.
salida

Ahora observa el código a continuación:

try:
y = 1 / 0
except ArithmeticError:
print("Uuuppsss...")

print("FIN.")

Algo ha cambiado: hemos reemplazado ZeroDivisionError con ArithmeticError .

Ya se sabe que ArithmeticError es una clase general que incluye (entre otras) la
excepción ZeroDivisionError .

Por lo tanto, la salida del código permanece sin cambios. Pruébalo.

Esto también significa que reemplazar el nombre de la excepción ya sea


con Exception o BaseException no cambiará el comportamiento del programa.

Vamos a resumir:

• Cada excepción generada cae en la primer coincidencia.


• La coincidencia correspondiente no tiene que especificar exactamente la misma
excepción, es suficiente que la excepción sea más general (más abstracta) que la
generada.

try:
y = 1 / 0
except ZeroDivisionError:
print("Uuupsss...")

print("FIN.")

2.7.1.3 La anatomía de las excepciones

Excepciones: continuación
Observa 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.
salida

¿Cambiará algo si intercambiamos los dos bloques except ? Justo como aquí abajo:

try:
y = 1 / 0
except ArithmeticError:
print("¡Problema Aritmético!")
except ZeroDivisionError:
print("¡División entre Cero!")

print("FIN.")

El cambio es radical: la salida del código es ahora:

¡Problema Aritmético!
FIN.
salida

¿Por qué, si la excepción generada 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.")

2.7.1.4 La anatomía de las excepciones

Excepciones: continuación
Si deseas manejar dos o más excepciones de la misma manera, puedes usar la siguiente
sintaxis:

try:
:
except (exc1, exc2):
:

Simplemente tienes que poner todos los nombres de las excepciones empleadas en una
lista separada por comas y no olvidar los paréntesis.

Si una excepción es generada 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 generada dentro de la función badfun() , y la función en sí
misma se encarga de ella.

La salida del programa es:

¡Problema Aritmético!
FIN.
salida

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 bad_fun(n):
return 1 / n

try:
bad_fun(0)
except ArithmeticError:
print("¿Que pasó? ¡Se generó 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:

¿Qué pasó? ¡Se generó una excepción!


FIN.
salida

Nota: la excepción generada 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 bad_fun(n):
try:
return 1 / n
except ArithmeticError:
print("¡Problema Aritmético!")
return None

bad_fun(0)

print("FIN.")

2.7.1.5 La anatomía de las excepciones | raise

Excepciones: continuación
La instrucción raise genera la excepción especificada denominada exc como si fuese
generada de manera natural:

raise exc
Nota: raise es una palabra clave reservada.

La instrucción te 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 bad_fun(n):
raise ZeroDivisionError

try:
bad_fun(0)
except ArithmeticError:
print("¿Que pasó? ¿Un error?")

print("FIN.")

2.7.1.6 La anatomía de las excepciones | raise

Excepciones: continuación
La instrucción raise también se puede utilizar de la siguiente manera (toma en cuenta la
ausencia del nombre de la excepción):

raise

Existe una seria restricción: esta variante de la instrucción raise puede ser
utilizada solamente dentro del bloque 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.
salida
def bad_fun(n):
try:
return n / 0
except:
print("¡Lo hice otra vez!")
raise

try:
bad_fun(0)
except ArithmeticError:
print("¡Ya veo!")

print("FIN.")

2.7.1.7 La anatomía de las excepciones | assert

Excepciones: continuación
Ahora es un buen momento para mostrarte otra instrucción de Python,
llamada assert (afirmar). Esta es una palabra clave reservada.

assert expression

¿Cómo funciona?

• Se evalúa la expresión.
• Si la expresión se evalúa como True (Verdadera), o un valor numérico distinto de
cero, o una cadena no vacía, o cualquier otro valor diferente de None , no hará
nada más.
• De lo contrario, automáticamente e inmediatamente se 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. Observa el código en el editor. Ejecútalo.

El programa se ejecuta sin problemas si se ingresa un valor numérico válido mayor o


igual a cero; de lo contrario, se detiene y emite el siguiente mensaje:

Traceback (most recent call last):

File ".main.py", line 4, in


assert x >= 0.0
AssertionError
salida
import math

x = float(input("Ingresa un número: "))


assert x >= 0.0

x = math.sqrt(x)

print(x)

2.7.1.8 RESUMEN DE SECCIÓN

Puntos Clave

1. No se puede agregar más de un bloque except sin nombre después de los bloques
con nombre.

:
# El código que siempre corre suavemente.
:
try:
:
# Código arriesgado.
:
except Except_1:
# La gestión de la crisis se lleva a cabo aquí.
except Except_2:
# Salvamos el mundo aqui.
except:
# Todos los demás problemas caen aquí.
:
# De vuelta a la normalidad.
:

2. Todas las excepciones de Python predefinidas forman una jerarquía, es decir, algunas
de ellas son más generales (la llamada BaseException es la más general) mientras que
otras son más o menos concretas (por ejemplo, IndexError es más concreta
que LookupError ).

No debes poner excepciones más concretas antes de las más generales dentro de la
misma secuencia de bloques except . Por ejemplo, puedes hacer esto:

try:
# Código arriesgado.
except IndexError:
# Solucionando problemas con listas.
except LookupError:
# Lidiando con búsquedas erróneas.

Pero no hagas esto (a menos de que estés absolutamente seguro de que quieres que
alguna parte de tu código sea inaccesible).

try:
# Código arriesgado.
except LookupError:
# Lidiando con búsquedas erróneas.
except IndexError:
# Nunca llegarás aquí.

3. La sentencia de Python raise ExceptionName puede generar una excepción bajo


demanda. La misma sentencia pero sin ExceptionName, se puede usar solamente dentro
del bloque try , y genera la misma excepción que se está manejando actualmente.

4. La sentencia de Python assert expression evalúa la expresión y genera la


excepción AssertError cuando la expresión es igual a cero, una cadena vacía o None .
Puedes usarla para proteger algunas partes críticas de tu código de datos devastadores.

Ejercicio 1

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


try:
print(1/0)
except ZeroDivisionError:
print("cero")
except ArithmeticError:
print("arit")
except:
print("algo")

Revisar

cero

Ejercicio 2

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

try:
print(1/0)
except ArithmeticError:
print("arit")
except ZeroDivisionError:
print("cero")
except:
print("algo")

Revisar

arit

Ejercicio 3

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

def foo(x):
assert x
return 1/x

try:
print(foo(0))
except ZeroDivisionError:
print("cero")
except:
print("algo")

Revisar

algo
2.8.1.1 Excepciones útiles

Excepciones integradas
Te mostraremos una breve lista de las excepciones más útiles. Si bien puede sonar
extraño llamar "útil" a una cosa o un fenómeno que es un signo visible de una falla o
retroceso, como sabes, errar es humano y si algo puede salir mal, saldrá mal.

Las excepciones son tan rutinarias y normales como cualquier otro aspecto de la vida de
un programador.

Para cada excepción, te mostraremos:

• Su nombre.
• Su ubicación en el árbol de excepciones.
• Una breve descripción.
• Un fragmento de código conciso que muestre las circunstancias en las que se
puede generar la excepción.

Hay muchas otras excepciones para explorar: simplemente no tenemos el espacio para
revisarlas todas aquí.

ArithmeticError
Ubicación: BaseException ← Exception ← ArithmeticError

Descripción: una excepción abstracta que incluye todas las excepciones causadas por
operaciones aritméticas como división cero o dominio inválido de un argumento.

AssertionError
Ubicación: BaseException ← Exception ← AssertionError

Descripción: una excepción concreta generada por la instrucción assert cuando su


argumento se evalúa a 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 que angle != 90 + k * 180


assert angle % 180 != 90
print(tan(radians(angle)))
BaseException
Ubicación: BaseException

Descripción: la excepción más general (abstracta) de todas las excepciones de Python:


todas las demás excepciones se incluyen en esta; se puede decir que las siguientes
dos excepciones son equivalentes: except: y except BaseException: .

IndexError
Ubicación: BaseException ← Exception ← LookupError ← IndexError

Descripción: una excepción concreta que surge cuando se intenta acceder al elemento de
una secuencia inexistente (por ejemplo, el elemento de una lista).

Código:

# El codigo muestra una forma extravagante


# de dejar el bucle.

the_list = [1, 2, 3, 4, 5]
ix = 0
do_it = True

while do_it:
try:
print(the_list[ix])
ix += 1
except IndexError:
do_it = False

print('Listo')

2.8.1.2 Excepciones útiles


KeyboardInterrupt
Ubicación: BaseException ← KeyboardInterrupt

Descripción: una excepción concreta que surge cuando el usuario usa un atajo de teclado
diseñado para terminar la ejecución de un programa (Ctrl-C en la mayoría de los Sistemas
Operativos); si manejar esta excepción no conduce a la terminación del programa, el
programa continúa su ejecución.

Nota: esta excepción no se deriva de la clase Exception. Ejecuta el programa en IDLE.

Código:
# Este código no puede ser terminado
# presionando Ctrl-C.

from time import sleep

seconds = 0

while True:
try:
print(seconds)
seconds += 1
sleep(1)
except KeyboardInterrupt:
print("¡No hagas eso!")

LookupError
Ubicación: BaseException ← Exception ← LookupError

Descripción: una excepción abstracta que incluye todas las excepciones causadas por
errores resultantes de referencias no válidas a diferentes colecciones (listas, diccionarios,
tuplas, etc.).

MemoryError
Ubicación: BaseException ← Exception ← MemoryError

Descripción: se genera una excepción concreta cuando no se puede completar una


operación debido a la falta de memoria libre.

Código:

# Este código causa la excepción MemoryError.


# Advertencia: el ejecutar este código puede afectar tu Sistema
Operativo.
# ¡No lo ejecutes en entornos de producción!

string = 'x'
try:
while True:
string = string + string
print(len(string))
except MemoryError:
print('¡Esto no es gracioso!')

OverflowError
Ubicación: BaseException ← Exception ← ArithmeticError ← OverflowError
Descripción: una excepción concreta que surge cuando una operación produce un
número demasiado grande para ser almacenado con éxito.

Código:

# El código imprime los valores subsequentes


# de exp(k), k = 1, 2, 4, 8, 16, ...

from math import exp

ex = 1

try:
while True:
print(exp(ex))
ex *= 2
except OverflowError:
print('El número es demasiado grande.')

2.8.1.3 Excepciones útiles


ImportError
Ubicación: BaseException ← Exception ← StandardError ← ImportError

Descripción: se genera una excepción concreta cuando falla una operación de


importación.

Código:

# Una de estas importaciones fallará, ¿cuál será?

try:
import math
import time
import abracadabra

except:
print('Una de tus importaciones ha fallado.')

KeyError
Ubicación: BaseException ← Exception ← LookupError ← KeyError

Descripción: una excepción concreta que se genera cuando intentas acceder al elemento
inexistente de una colección (por ejemplo, el elemento de un diccionario).

Código:
# ¿Cómo abusar del diccionario
# y cómo lidiar con ello?

dictionary = {'a': 'b', 'b': 'c', 'c': 'd'}


ch = 'a'

try:
while True:
ch = dictionary[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 usarlas para proteger tu código de
accidentes graves, pero también tienes que aprender a sumergirte en ellas, 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.

2.8.1.4 Leer enteros de forma segura


LABORATORIO
Tiempo Estimado
15-25 minutos

Nivel de dificultad
Medio

Objetivos
• Mejorar las habilidades del alumno al definir funciones.
• Utilizar excepciones para proporcionar un entorno de entrada más 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 deberá:
• Aceptar tres argumentos: una entrada, un límite inferior aceptable y un límite
superior aceptable.
• Si el usuario ingresa una cadena que no es un valor entero, la función debe emitir
el mensaje Error: entrada incorrecta , y solicitará al usuario que ingrese el
valor nuevamente.
• Si el usuario ingresa un número que está fuera del rango especificado, la función
debe emitir el mensaje Error: el valor no está dentro del rango
permitido (min..max) y solicitará al usuario que ingrese el valor nuevamente.
• Si el valor de entrada es válido, será regresado como resultado.

Datos de Prueba
Prueba tu código cuidadosamente.

Así es como la función debería reaccionar ante la entrada del usuario:

Ingresa un número entre -10 a 10: 100


Error: el valor no está dentro del rango permitido (-10..10)
Ingresa un número entre -10 a 10: asd
Error: entrada incorrecta
Ingresa un número entre -10 a 10: 1
El número es: 1

def read_int(prompt, min, max):


#
# Escribe tu código aquí.
#

v = read_int("Ingresa un numero entre -10 a 10: ", -10, 10)

print("El número es:", v)

2.8.1.5 RESUMEN DE SECCIÓN

Puntos Clave
1. Algunas excepciones integradas abstractas de Python son:

• ArithmeticError .
• BaseException .
• LookupError .

2. Algunas excepciones integradas concretas de Python son:

• AssertionError .
• ImportError .
• IndexError .
• KeyboardInterrupt .
• KeyError .
• MemoryError .
• OverflowError .

Ejercicio 1

¿Cuál de las excepciones se utilizará para proteger al código de ser interrumpido por el
uso del teclado?

Revisar

KeyboardInterrupt

Ejercicio 2

¿Cuál es el nombre de la más general de todas las excepciones de Python?

Revisar

BaseException

Ejercicio 3

¿Cuál de las excepciones será generada a través de la siguiente evaluación fallida?

huge_value = 1E250 ** 2

Revisar

OverflowError

2.8.1.6 Finalización del Módulo

¡Felicitaciones! Has completado FP2: 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. A continuación, se incluye un breve resumen de
los objetivos que has cubierto y con los que se te familiarizó en el Módulo 2:

• Caracteres, cadenas y estándares de codificación.


• La naturaleza de las cadenas en Python; cadenas frente a listas: similitudes y
diferencias.
• Métodos de lista y cadena.
• Manejo de errores en Python.
• Controlar el flujo de errores usando try y except.
• La jerarquía de excepciones; revisión de las excepciones más útiles.
Ahora estás listo para realizar el cuestionario del módulo e intentar el desafío final: el
Examen del Módulo 2, el cual te ayudará a evaluar lo que has aprendido hasta ahora.

Fundamentos de Python 2 - Módulo 3

Fundamentos de Python 2:
Módulo 3
Programación Orientada a Objetos

En este módulo, aprenderás sobre:

• Conceptos básicos de programación orientada a objetos (POO).


• Las diferencias entre el enfoque procedimental y el orientado a objetos
(motivaciones y beneficios).
• Clases, objetos, propiedades y métodos.
• Cómo diseñar clases reutilizables y crear objetos.
• Herencia y polimorfismo.
• Las excepciones como objetos.

3.1.1.1 Los fundamentos de la POO

Los conceptos básicos del enfoque orientado a objetos


Demos un paso fuera de la programación y las computadoras, y analicemos temas de
programación orientada a objetos.

Casi todos los programas y técnicas que has utilizado hasta ahora pertenecen al estilo de
programación procedimental. Es cierto que has utilizado algunos objetos incorporados,
pero cuando nos referimos a ellos, se mencionan lo mínimo posible.

La programación procedimental fue el enfoque dominante para el desarrollo de software


durante décadas de TI, y todavía se usa en la actualidad. Además, no va a desaparecer en
el futuro, ya que funciona muy bien para proyectos específicos (en general, no muy
complejos y no grandes, pero existen muchas excepciones a esa regla).

El enfoque orientado a objetos es bastante joven (mucho más joven que el enfoque
procedimental) y es particularmente útil cuando se aplica a proyectos grandes y
complejos llevados a cabo por grandes equipos formados por muchos desarrolladores.
Este tipo de programación en un proyecto facilita muchas tareas importantes, por
ejemplo, dividir el proyecto en partes pequeñas e independientes y el desarrollo
independiente de diferentes elementos del proyecto.

Python es una herramienta universal para la programación procedimental y orientada a


objetos. Se puede utilizar con éxito en ambos enfoques.

Además, puedes crear muchas aplicaciones útiles, incluso si no se sabe nada sobre clases
y objetos, pero debes tener en cuenta que algunos de los problemas (por ejemplo, el
manejo de la interfaz gráfica de usuario) puede requerir un enfoque estricto de objetos.

Afortunadamente, la programación orientada a objetos es relativamente simple.

3.1.1.2 Los fundamentos de la POO

Enfoque procedimental frente al 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 puedan usar funciones?

Sí, los hay, los llamados métodos. Estas son funciones que se invocan desde dentro de los
datos, no junto con ellos. Si puedes ver esta distinción, has dado el primer paso en la
programación de objetos.

El enfoque orientado a objetos sugiere una forma de pensar completamente diferente.


Los datos y el código están encapsulados juntos en el mismo mundo, divididos en clases.
Cada clase es como una receta que se puede usar cuando quieres crear un objeto útil.
Puedes producir tantos objetos como necesites para resolver tu problema.

Cada objeto tiene un conjunto de rasgos (se denominan propiedades o atributos;


usaremos ambas palabras como sinónimos) y es capaz de realizar un conjunto de
actividades (que se denominan métodos).

Las recetas pueden modificarse si son inadecuadas para fines específicos y, en efecto,
pueden crearse nuevas clases. Estas nuevas clases heredan propiedades y métodos de
los originales, y generalmente agregan algunos nuevos, creando nuevas herramientas
más específicas.

Los objetos son encarnaciones de las ideas expresadas en clases, como un pastel de
queso en tu plato, es una encarnación de la idea expresada en una receta impresa en un
viejo libro de cocina.

Los objetos interactúan entre sí, intercambian datos o activan sus métodos. Una clase
construida adecuadamente (y, por lo tanto, sus objetos) puede proteger los datos
sensibles y ocultarlos de modificaciones no autorizadas.

No existe un límite claro entre los datos y el código: viven como uno solo dentro de los
objetos.

Todos estos conceptos no son tan abstractos como pudieras pensar al principio. Por el
contrario, todos están tomados de experiencias de la vida real y, por lo tanto, son
extremadamente útiles en la programación de computadoras: no crean vida
artificial reflejan hechos reales, relaciones y circunstancias.

3.1.1.3 Los fundamentos de la POO

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) están relacionados por una sola característica importante: la capacidad de
moverse. Puedes argumentar que un perro también se mueve; ¿Es un perro un vehículo?
No lo es. Tenemos que mejorar la definición, es decir, enriquecerla con otros criterios,
distinguir los vehículos de otros seres y crear una conexión más fuerte. Consideremos las
siguientes circunstancias: los vehículos son entidades creadas artificialmente que se
utilizan para el transporte, movidos por fuerzas de la naturaleza y dirigidos (conducidos)
por humanos.

Según esta definición, un perro no es un vehículo.

La clase Vehículos es muy amplia. Tenemos que definir clases especializadas. Las clases
especializadas son las subclases. La clase Vehículos será una superclase para todas ellas.

Nota: la jerarquía crece de arriba hacia abajo, como raíces de árboles, no ramas. La clase
más general y más amplia siempre está en la parte superior (la superclase) mientras que
sus descendientes se encuentran abajo (las subclases).

A estas alturas, probablemente puedas señalar algunas subclases potenciales para la


superclase Vehículos. Hay muchas clasificaciones posibles. Elegimos subclases basadas
en el medio ambiente y decimos que hay (al menos) cuatro subclases:

• Vehículos Terrestres.
• Vehículos Acuáticos.
• Vehículos Aéreos.
• Vehículos Espaciales.

En este ejemplo, discutiremos solo la primera subclase: Vehículos Terrestres. Si lo deseas,


puedes continuar con las clases restantes.

Los vehículos terrestres pueden dividirse aún más, según el método con el que impactan
el suelo. Entonces, podemos enumerar:

• Vehículos con ruedas.


• Vehículos oruga.
• Aerodeslizadores.
La figura ilustra la jerarquía que hemos creado.

Ten en cuenta la dirección de las flechas: siempre apuntan a la superclase. La clase de


nivel superior es una excepción: no tiene su propia superclase.

3.1.1.4 Los fundamentos de la POO

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.
• Aves.
• 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.

3.1.1.5 Los fundamentos de la POO

¿Qué es un objeto?
Una clase (entre otras definiciones) es un conjunto de objetos. Un objeto es un ser
perteneciente a una clase.
Un objeto es una encarnación de los requisitos, rasgos y cualidades asignados a una clase
específica. Esto puede sonar simple, pero ten en cuenta las siguientes circunstancias
importantes. Las clases forman una jerarquía.

Esto puede significar que un objeto que pertenece a una clase específica pertenece a
todas las superclases al mismo tiempo. También puede significar que cualquier objeto
perteneciente a una superclase puede no pertenecer a ninguna de sus subclases.

Por ejemplo: cualquier automóvil personal es un objeto que pertenece a la


clase Vehículos Terrestres. También significa que el mismo automóvil pertenece a todas
las superclases de su clase local; por lo tanto, también es miembro de la clase Vehículos.

Tu perro (o tu gato) es un objeto incluido en la clase Mamíferos Domesticados, lo que


significa explícitamente que también está incluido en la clase Animales.

Cada subclase es más especializada (o más específica) que su superclase. Por el contrario,
cada superclase es más general (más abstracta) que cualquiera de sus subclases.

Ten en cuenta que hemos supuesto que una clase solo puede tener una superclase; esto
no siempre es cierto, pero discutiremos este tema más adelante.

Herencia
Definamos uno de los conceptos fundamentales de la programación de objetos,
llamado herencia. Cualquier objeto vinculado a un nivel específico de una jerarquía de
clases hereda todos los rasgos (así como los requisitos y cualidades) definidos dentro de
cualquiera de las superclases.

La clase de inicio del objeto puede definir nuevos rasgos (así como requisitos y
cualidades) que serán heredados por cualquiera de sus superclases.

No deberías tener ningún problema para hacer coincidir esta regla con ejemplos
específicos, ya sea que se aplique a animales o vehículos.
3.1.1.6 Los fundamentos de la POO

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

Existe 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 está definiendo el nombre del objeto.


• Un adjetivo: probablemente se está definiendo una propiedad del objeto.
• Un verbo: probablemente se está definiendo una actividad del objeto.

Dos ejemplos deberían servir como un buen ejemplo:

• Un Cadillac rosa pasó rápidamente.

Nombre del objeto = Cadillac


Clase = Vehículos con ruedas
Propiedad = Color (rosa)
Actividad = Pasar (rápidamente)

• Max es un gato grande que duerme todo el día.

Nombre del objeto = Max


Clase = Gato
Propiedad = Tamaño (Grande)
Actividad = Dormir (Todo el día)
3.1.1.7 Los fundamentos de la POO

Tu primera clase
La programación orientada a objetos es el arte de definir y expandir clases. Una clase es
un modelo de una parte muy específica de la realidad, que refleja las propiedades y
actividades que se encuentran en el mundo real.

Las clases definidas al principio son demasiado generales e imprecisas para cubrir el
mayor número posible de casos reales.

No hay obstáculo para definir nuevas subclases más precisas. Heredarán todo de su
superclase, por lo que el trabajo que se utilizó para su creación no se desperdicia.

La nueva clase puede agregar nuevas propiedades y nuevas actividades y, por lo tanto,
puede ser más útil en aplicaciones específicas. Obviamente, se puede usar como una
superclase para cualquier número de subclases recién creadas.

El proceso no necesita tener un final. Puedes crear tantas clases como necesites.

La clase que se define no tiene nada que ver con el objeto: la existencia de una clase no
significa que ninguno de los objetos compatibles se creará automáticamente. La clase en
sí misma no puede crear un objeto: debes crearlo tu mismo y Python te permite hacerlo.

Es hora de definir la clase más simple y crear un objeto. Analiza el siguiente ejemplo:

class TheSimplestClass:
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 le dará nombre a la clase (nota: no lo confundas con
el nombre del objeto: estas son dos cosas diferentes).
A continuación, se agregan dos puntos ( : ), como clases, como funciones, forman su
propio bloque anidado. El contenido dentro del bloque define todas las propiedades y
actividades de la clase.

La palabra clave reservada pass llena la clase con nada. No contiene ningún método ni
propiedades.

Tu primer objeto
La clase recién definida se convierte en una herramienta que puede crear nuevos objetos.
La herramienta debe usarse explícitamente, bajo demanda.

Imagina que deseas crear un objeto (exactamente uno) de la clase TheSimplestClass .

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:

my_first_object = TheSimplestClass()

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.

3.1.1.8 RESUMEN DE SECCIÓN

Puntos Clave
1. Una clase es una idea (más o menos abstracta) que se puede utilizar para crear varias
encarnaciones; una encarnación de este tipo se denomina objeto.

2. Cuando una clase se deriva de otra clase, su relación se denomina herencia. La clase
que deriva de la otra clase se denomina subclase. El segundo lado de esta relación se
denomina superclase. Una forma de presentar dicha relación es en un diagrama de
herencia, donde:

• Las superclases siempre se presentan encima de sus subclases.


• Las relaciones entre clases se muestran como flechas dirigidas desde la subclase
hacia su superclase.

3. Los objetos están equipados con:

• Un nombre que los identifica y nos permite distinguirlos.


• Un conjunto de propiedades (el conjunto puede estar vacío).
• Un conjunto de métodos (también puede estar vacío).

4. Para definir una clase de Python,se necesita usar la palabra clave reservada class . Por
ejemplo:

class This_Is_A_Class:
pass

5. Para crear un objeto de la clase previamente definida, se necesita usar la clase como si
fuera una función. Por ejemplo:

this_is_an_object = This_Is_A_Class()

Ejercicio 1

Si asumimos que pitones, víboras y cobras son subclases de la misma superclase, ¿cómo
la llamarías?

Revisar
Serpiente, reptil, vertebrado, animal: todas estas respuestas son aceptables.

Ejercicio 2

Intenta nombrar algunas subclases de las clase Pitón.

Revisar
Pitón india, Pitón de Roca Sfricana, Pitón Bola, Pitón Birmana: la lista es larga.

Ejercicio 3

¿Puedes usar la palabra "class" para darle nombre a alguna de tus clases?

Revisar
¡No, no puedes, class es una palabra clave reservada!
3.2.1.1 Un corto viaje desde el enfoque procedimental hacia el
orientado a objetos

¿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 inglés).

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
como hacerlo en dos enfoques independientes: de manera procedimental y orientado a
objetos.

Comencemos con el primero.


3.2.1.2 Un corto viaje desde el enfoque procedimental hacia el
orientado a objetos

La pila: el enfoque procedimental


Primero, debes decidir como 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:

stack = []

Estamos listos para definir una función que coloca 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 retorna 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):
stack.append(val)

Ahora es tiempo de que una función quite un valor de la pila. Así es como puedes
hacerlo:

• El nombre de la función es pop.


• La función no obtiene ningún parámetro.
• La función devuelve el valor tomado de la pila.
• La función lee el valor de la parte superior de la pila y lo elimina.

La función esta aqui:

def pop():
val = stack[-1]
del stack[-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
salida

Pruébalo.

stack = []

def push(val):
stack.append(val)

def pop():
val = stack[-1]
del stack[-1]
return val

push(3)
push(2)
push(1)

print(pop())
print(pop())
print(pop())

3.2.1.3 Un corto viaje desde el enfoque procedimental hacia el


orientado a objetos

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:

stack[0] = 0

El funcionamiento de la pila estará completamente desorganizado.

• También puede suceder que un día necesites más de una pila; tendrás que crear
otra lista para el almacenamiento de la pila, y probablemente otras
funciones push y pop .
• También puede suceder que no solo necesites funciones push y pop , pero
también algunas otras funciones; ciertamente podrías implementarlas, pero
intenta imaginar qué sucedería si tuvieras docenas de pilas implementadas por
separado.

El enfoque orientado a objetos ofrece soluciones para cada uno de los problemas
anteriores. Vamos a nombrarlos primero:

• La capacidad de ocultar (proteger) los valores seleccionados contra el acceso no


autorizado se llama encapsulamiento; no se puede acceder a los valores
encapsulados ni modificarlos si deseas utilizarlos exclusivamente.

• Cuando tienes una clase que implementa todos los comportamientos de pila
necesarios, puedes producir tantas pilas como desees; no necesitas copiar ni
replicar ninguna parte del código.

• La capacidad de enriquecer la pila con nuevas funciones proviene de la herencia;


puedes crear una nueva clase (una subclase) que herede todos los rasgos
existentes de la superclase y agregar 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.
3.2.1.4 Un corto viaje desde el enfoque procedimental hacia el
orientado a 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 como poner la lista en la clase.

Comencemos desde el principio: así es como comienza la pila orientada a objetos:

class Stack:

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?

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

Dicha 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 Stack:
def __init__(self):
print("¡Hola!")

stack_object = Stack()
Expliquemos más a detalle:

• El nombre del constructor es siempre __init__ .


• Tiene que tener al menos un parámetro (discutiremos esto más adelante); 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!
salida

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 Stack: # Definiendo la clase de la pila.


def __init__(self): # Definiendo la función del constructor.
print("¡Hola!")

stack_object = Stack() # Instanciando el objeto.

3.2.1.5 Un corto viaje desde el enfoque procedimental hacia el


orientado a objetos

La pila, el enfoque orientado a objetos: continuación


Cualquier cambio que realices dentro del constructor que modifique el estado del
parámetro self se verá reflejado en el objeto recien creado.

Esto significa que puedes agregar cualquier propiedad al objeto y la propiedad


permanecerá allí hasta que el objeto termine su vida o la propiedad se elimine
explícitamente.

Ahora agreguemos solo una propiedad al nuevo objeto, una lista para la pila. La
nombraremos stack_list .

Justo como aqui:

class Stack:
def __init__(self):
self.stack_list = []

stack_object = Stack()
print(len(stack_object.stack_list))

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 stack_list 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?

Si, por supuesto: el código produce el siguiente resultado:

0
salida

Esto no es lo que queremos de la pila. Nosotros queremos


que stack_list este escondida del mundo exterior. ¿Es eso posible?

Si, y es simple, pero no muy intuitivo.

class Stack:
def __init__(self):
self.stack_list = []

stack_object = Stack()
print(len(stack_object.stack_list))

3.2.1.6 Un corto viaje desde el enfoque procedimental hacia el


orientado a objetos

La pila, el enfoque orientado a objetos: continuación


Echa un vistazo: hemos agregado dos guiones bajos antes del nombre stack_list , nada
mas:
class Stack:
def __init__(self):
self.__stack_list = []

stack_object = Stack()
print(len(stack_object.__stack_list))

El cambio invalida el programa.

¿Por qué?

Cuando cualquier componente de la clase tiene un nombre que comienza con dos
guiones bajos ( __ ), se vuelve privado, esto significa que solo se puede acceder desde
dentro de 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 generada.

class Stack:
def __init__(self):
self.__stack_list = []

stack_object = Stack()
print(len(stack_object.__stack_list))

3.2.1.7 Un corto viaje desde el enfoque procedimental hacia el


orientado a objetos

El enfoque orientado a objetos: una pila desde cero


Ahora es el momento de que las dos funciones (métodos) implementen las
operaciones push y pop. Python supone que una función de este tipo debería
estar inmersa dentro del cuerpo de la clase, como el constructor.

Queremos invocar estas funciones para agregar(push) y quitar(pop) valores de la


pila. Esto significa que ambos deben ser accesibles para el usuario de la clase (en
contraste con la lista previamente construida, que está oculta para los usuarios de la
clase ordinaria).

Tal componente es llamado público, 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 Stack:
def __init__(self):
self.__stack_list = []

def push(self, val):


self.__stack_list.append(val)

def pop(self):
val = self.__stack_list[-1]
del self.__stack_list[-1]
return val

stack_object = Stack()

stack_object.push(3)
stack_object.push(2)
stack_object.push(1)

print(stack_object.pop())
print(stack_object.pop())
print(stack_object.pop())

Sin embargo, hay algo realmente extraño en el código. Las funciones parecen familiares,
pero tienen más parámetros que sus contrapartes procedimentales.

Aquí, ambas funciones tienen un parámetro llamado self en la primera posición de la


lista de parámetros.

¿Es necesario? Si, lo es.

Todos los métodos deben tener este parámetro. Desempeña el mismo papel que el
primer parámetro constructor.

Permite que el método acceda a entidades (propiedades y actividades / métodos) del


objeto. No puedes omitirlo. Cada vez que Python invoca un método, envía implícitamente
el objeto actual como el primer argumento.

Esto significa que el método está obligado a tener al menos un parámetro, que Python
mismo utiliza, no tienes ninguna influencia sobre el.

Si tu método no necesita ningún parámetro, este debe especificarse de todos modos. Si


está diseñado para procesar solo un parámetro, debes especificar dos, ya que la función
del primero sigue siendo la misma.

Hay una cosa más que requiere explicación: la forma en que se invocan los métodos
desde la variable __stack_list .

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 __stack_list → self.__stack_list .
• Con __stack_list lista para ser usada, puedes realizar el tercer y último paso
→ self.__stack_list.append(val) .

La declaración de la clase está completa y se han enumerado todos sus componentes. La


clase está lista para usarse.

class Stack:
def __init__(self):
self.__stack_list = []

def push(self, val):


self.__stack_list.append(val)

def pop(self):
val = self.__stack_list[-1]
del self.__stack_list[-1]
return val

stack_object = Stack()

stack_object.push(3)
stack_object.push(2)
stack_object.push(1)

print(stack_object.pop())
print(stack_object.pop())
print(stack_object.pop())

3.2.1.8 Un corto viaje desde el enfoque procedimental hacia el


orientado a objetos

El enfoque orientado a objetos: una pila desde cero


Tener tal clase abre nuevas posibilidades. Por ejemplo, ahora puedes hacer que más de
una pila se comporte de la misma manera. Cada pila tendrá su propia copia de datos
privados, pero utilizará el mismo conjunto de métodos.

Esto es exactamente lo que queremos para este ejemplo.

Analiza el código:

class Stack:
def __init__(self):
self.__stack_list = []
def push(self, val):
self.__stack_list.append(val)

def pop(self):
val = self.__stack_list[-1]
del self.__stack_list[-1]
return val

stack_object_1 = Stack()
stack_object_2 = Stack()

stack_object_1.push(3)
stack_object_2.push(stack_object_1.pop())

print(stack_object_2.pop())

Existen dos pilas creadas a partir de la misma clase base. Trabajan independientemente.
Puedes crear más si quieres.

Ejecuta el código en el editor y observa que sucede. Realiza tus propios experimentos.

class Stack:
def __init__(self):
self.__stack_list = []

def push(self, val):


self.__stack_list.append(val)

def pop(self):
val = self.__stack_list[-1]
del self.__stack_list[-1]
return val

stack_object_1 = Stack()
stack_object_2 = Stack()

stack_object_1.push(3)
stack_object_2.push(stack_object_1.pop())

print(stack_object_2.pop())

3.2.1.9 Un corto viaje desde el enfoque procedimental hacia el


orientado a objetos
El enfoque orientado a objetos: una pila desde cero
(continuación)
Analiza el fragmento de código a continuación: hemos creado tres objetos de la
clase Stack . Después, hemos hecho malabarismos. Intenta predecir el valor que se
muestra en la pantalla.

class Stack:
def __init__(self):
self.__stack_list = []

def push(self, val):


self.__stack_list.append(val)

def pop(self):
val = self.__stack_list[-1]
del self.__stack_list[-1]
return val

little_stack = Stack()
another_stack = Stack()
funny_stack = Stack()

little_stack.push(1)
another_stack.push(little_stack.pop() + 1)
funny_stack.push(another_stack.pop() - 2)

print(funny_stack.pop())

Entonces, ¿cuál es el resultado? Ejecuta el programa y comprueba si tenías razón.

class Stack:
def __init__(self):
self.__stack_list = []

def push(self, val):


self.__stack_list.append(val)

def pop(self):
val = self.__stack_list[-1]
del self.__stack_list[-1]
return val

# Ingresa código aquí.

3.2.1.10 Un corto viaje desde el enfoque procedimental hacia el


orientado a objetos
El enfoque orientado a objetos: una pila desde cero
(continuación)
Ahora vamos un poco mas lejos. Vamos a agregar una nueva clase para manejar pilas.

La nueva clase debería poder evaluar la suma de todos los elementos almacenados
actualmente en la pila.

No queremos modificar la pila previamente definida. Ya es lo suficientemente buena en


sus aplicaciones, y no queremos que cambie de ninguna manera. Queremos una nueva
pila con nuevas capacidades. En otras palabras, queremos construir una subclase de la ya
existente clase Stack .

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 AddingStack(Stack):
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. Será 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 como
hacerlo, pero hay algo realmente intrigante dentro del constructor. Echa un vistazo:

class AddingStack(Stack):
def __init__(self):
Stack.__init__(self)
self.__sum = 0

La segunda línea del cuerpo del constructor crea una propiedad llamada __sum ,
almacenará el total de todos los valores de la pila.

Pero la línea anterior se ve diferente. ¿Qué hace? ¿Es realmente necesaria? Sí lo es.
Al contrario de muchos otros lenguajes, Python te obliga a invocar explícitamente el
constructor de una superclase. Omitir este punto tendrá efectos nocivos: el objeto se
verá privado de la lista __stack_list . Tal pila no funcionará correctamente.

Esta es la única vez que puedes invocar a cualquiera de los constructores disponibles
explícitamente; se puede hacer dentro del constructor de la superclase.

Ten en cuenta la sintaxis:

• Se especifica el nombre de la superclase (esta es la clase cuyo constructor se


desea ejecutar).
• Se pone un punto ( . ) después del nombre.
• Se especifica el nombre del constructor.
• Se debe señalar al objeto (la instancia de la clase) que debe ser inicializado por el
constructor; es por eso que se debe especificar el argumento y utilizar la
variable self aquí; recuerda: invocar cualquier método (incluidos los
constructores) desde fuera de la clase nunca requiere colocar el
argumento self en la lista de argumentos, invocar un método desde dentro de la
clase exige el uso explícito del argumento self , y tiene que ser el primero en la
lista.

Nota: generalmente es una práctica recomendada invocar al constructor de la superclase


antes de cualquier otra inicialización que desees realizar dentro de la subclase. Esta es la
regla que hemos seguido en el código.

class Stack:
def __init__(self):
self.__stack_list = []

def push(self, val):


self.__stack_list.append(val)

def pop(self):
val = self.__stack_list[-1]
del self.__stack_list[-1]
return val

class AddingStack(Stack):
def __init__(self):
Stack.__init__(self)
self.__sum = 0

3.2.1.11 Un corto viaje desde el enfoque procedimental hacia el


orientado a objetos
El enfoque orientado a objetos: una pila desde cero
(continuación)
En segundo lugar, agreguemos dos métodos. Pero, ¿realmente estamos agregándolos? Ya
tenemos estos métodos en la superclase. ¿Podemos hacer algo así?

Si podemos. Significa que vamos a cambiar la funcionalidad de los métodos, no sus


nombres. Podemos decir con mayor precisión que la interfaz (la forma en que se
manejan los objetos) de la clase permanece igual al cambiar la implementación al mismo
tiempo.

Comencemos con la implementación de la función push . Esto es lo que esperamos de la


función:

• Agregar el valor a la variable __sum .


• Agregar el valor a la pila.

Nota: la segunda actividad ya se implementó dentro de la superclase, por lo que


podemos usarla. Además, tenemos que usarla, ya que no hay otra forma de acceder a la
variable __stackList .

Así es como se mira el método push dentro de la subclase:


def push(self, val):
self.__sum += val
Stack.push(self, val)

Toma en cuenta la forma en que hemos invocado la implementación anterior del


método push (el disponible en la superclase):

• Tenemos que especificar el nombre de la superclase; esto es necesario para


indicar claramente la clase que contiene el método, para evitar confundirlo con
cualquier otra función del mismo nombre.
• Tenemos que especificar el objeto de destino y pasarlo como primer argumento
(no se agrega implícitamente a la invocación en este contexto).

Se dice que el método push ha sido anulado, el mismo nombre que en la superclase
ahora representa una funcionalidad diferente.

class Stack:
def __init__(self):
self.__stackList = []

def push(self, val):


self.__stackList.append(val)

def pop(self):
val = self.__stackList[-1]
del self.__stackList[-1]
return val

class AddingStack(Stack):
def __init__(self):
Stack.__init__(self)
self.__sum = 0

# Ingresa código aquí.

3.2.1.12 Un corto viaje desde el enfoque procedimental hacia el


orientado a objetos

El enfoque orientado a objetos: una pila desde cero


(continuación)
Esta es la nueva función pop :

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

Hasta ahora, hemos definido la variable __sum , pero no hemos proporcionado un


método para obtener su valor. Parece estar escondido. ¿Cómo podemos mostrarlo y que
al mismo tiempo que se proteja de modificaciones?

Tenemos que definir un nuevo método. Lo nombraremos get_sum . Su única tarea


será devolver el valor de __sum .

Aquí está:

def get_sum(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 Stack:
def __init__(self):
self.__stack_list = []

def push(self, val):


self.__stack_list.append(val)

def pop(self):
val = self.__stack_list[-1]
del self.__stack_list[-1]
return val

class AddingStack(Stack):
def __init__(self):
Stack.__init__(self)
self.__sum = 0

def get_sum(self):
return self.__sum

def push(self, val):


self.__sum += val
Stack.push(self, val)

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

stack_object = AddingStack()

for i in range(5):
stack_object.push(i)
print(stack_object.get_sum())

for i in range(5):
print(stack_object.pop())

3.2.1.13 RESUMEN DE SECCIÓN


Puntos Clave
1. Una pila es un objeto diseñado para almacenar datos utilizando el modelo LIFO. La
pila normalmente realiza al menos dos operaciones, llamadas push() y pop().
2. La implementación de la pila en un modelo procedimental plantea varios problemas
que pueden resolverse con las técnicas ofrecidas por la POO (Programación Orientada
a Objetos).

3. Un método de clase es en realidad una función declarada dentro de la clase y capaz de


acceder a todos los componentes de la clase.

4. La parte de la clase en Python responsable de crear nuevos objetos se


llama constructor y se implementa como un método de nombre __init__ .

5. Cada declaración de método de clase debe contener al menos un parámetro (siempre


el primero) generalmente denominado self , y es utilizado por los objetos para
identificarse a sí mismos.

6. Si queremos ocultar alguno de los componentes de una clase del mundo exterior,
debemos comenzar su nombre con __ . Estos componentes se denominan privados.

Ejercicio 1

Suponiendo que hay una clase llamada Snakes , escribe la primera línea de la declaración
de clase Python , expresando el hecho de que la nueva clase es en realidad una subclase
de Snake.

Revisar
class Python(Snakes):

Ejercicio 2

Algo falta en la siguiente declaración, ¿qué es?

class Snakes
def __init__():
self.sound = 'Sssssss'

Revisar

El constructor __init__() carece del parámetro obligatorio (deberíamos


llamarlo self para cumplir con los estándares).

Ejercicio 3

Modifica el código para garantizar que la propiedad venomous sea privada.

class Snakes
def __init__(self):
self.venomous = True

Revisar
El código debería verse como sigue:

class Snakes
def __init__(self):
self.__venomous = True

3.2.1.14 Pila Contadora


LABORATORIO

Tiempo Estimado
20-45 minutos

Nivel de Dificultad
Fácil/Medio

Objetivos
• Mejorar las habilidades del estudiante para definir clases.
• Emplear clases existentes para crear nuevas clases equipadas con nuevas
funcionalidades.

Escenario
Recientemente te mostramos cómo extender las posibilidades de Stack definiendo una
nueva clase (es decir, una subclase) que retiene todos los rasgos heredados y agrega
algunos nuevos.

Tu tarea es extender el comportamiento de la clase Stack de tal manera que la clase


pueda contar todos los elementos que son agregados (push) y quitados (pop). Emplea la
clase Stack que proporcionamos en el editor.

Sigue las sugerencias:

• Introduce una propiedad diseñada para contar las operaciones pop y nombrarla
de una manera que garantice que esté oculta.
• Inicializala a cero dentro del constructor.
• Proporciona un método que devuelva el valor asignado actualmente al contador
(nómbralo get_counter() ).
Completa el código en el editor. Ejecútalo para comprobar si tu código da como salida
100.

class Stack:
def __init__(self):
self.__stk = []

def push(self, val):


self.__stk.append(val)

def pop(self):
val = self.__stk[-1]
del self.__stk[-1]
return val

class CountingStack(Stack):
def __init__(self):
#
# Llena el constructor con acciones apropiadas.
#

def get_counter(self):
#
# Presenta el valor actual del contador al mundo.
#

def pop(self):
#
# Haz un pop y actualiza el contador.
#

stk = CountingStack()
for i in range(100):
stk.push(i)
stk.pop()
print(stk.get_counter())

3.2.1.15 Colas alias FIFO


LABORATORIO

Tiempo Estimado
20-45 minutos
Nivel de Dificultad
Fácil/Medio

Objetivos
• Mejorar las habilidades del estudiante para definir clases desde cero.
• Implementar estructuras de datos estándar como clases.

Escenario
Como ya sabes, una pila es una estructura de datos que realiza el modelo LIFO (último en
entrar, primero en salir). Es fácil y ya te has acostumbrado a ello perfectamente.

Probemos algo nuevo ahora. Una cola (queue) es un modelo de datos caracterizado por
el término FIFO: primero en entrar, primero en salir. Nota: una cola (fila) regular que
conozcas de las tiendas u oficinas de correos funciona exactamente de la misma manera:
un cliente que llegó primero también es el primero en ser atendido.

Tu tarea es implementar la clase Queue con dos operaciones básicas:

• put(elemento) , que coloca un elemento al final de la cola.


• get() , que toma un elemento del principio de la cola y lo devuelve como
resultado (la cola no puede estar vacía para realizarlo correctamente).

Sigue las sugerencias:

• Emplea una lista como tu almacenamiento (como lo hicimos con la pila).


• put() debe agregar elementos al principio de la lista, mientras que get() debe
eliminar los elementos del final de la lista.
• Define una nueva excepción llamada QueueError (elige una excepción de la cual
se derivará) y generala cuando get() intentes operar en una lista vacía.

Completa el código que te proporcionamos en el editor. Ejecútalo para comprobar si tu


salida es similar a la nuestra.

Salida Esperada
1
perro
False
Error de Cola

class QueueError(???): # Eligir la clase base para la nueva


excepción.
#
# Escribe código aquí.
#
class Queue:
def __init__(self):
#
# Escribe código aquí.
#

def put(self, elem):


#
# Escribe código aquí.
#

def get(self):
#
# Escribe código aquí.
#

que = Queue()
que.put(1)
que.put("perro")
que.put(False)
try:
for i in range(4):
print(que.get())
except:
print("Error de Cola")

3.2.1.16 Colas alias FIFO: parte 2


LABORATORIO
Tiempo Estimado
15-30 minutos

Nivel de Dificultad
Fácil/Medio

Objetivos
• Mejorar las habilidades del estudiante para definir subclases.
• Agregar nueva funcionalidad a una clase existente.
Escenario
Tu tarea es extender ligeramente las capacidades de la clase Queue . Queremos que tenga
un método sin parámetros que devuelva True si la cola está vacía y False de lo
contrario.

Completa el código que te proporcionamos en el editor. Ejecútalo para comprobar si


genera un resultado similar al nuestro.

A continuación, puedes copiar el código que usamos en el laboratorio anterior:

Revisar
class QueueError(IndexError):
pass

class Queue:
def __init__(self):
self.queue = []
def put(self,elem):
self.queue.insert(0,elem)
def get(self):
if len(self.queue) > 0:
elem = self.queue[-1]
del self.queue[-1]
return elem
else:
raise QueueError

Saluda Esperada
1
perro
False
Cola vacía

class QueueError(???):
pass

class Queue:
#
# Código del laboratorio anterior.
#

class SuperQueue(Queue):
#
# Escribe código nuevo aquí.
#

que = SuperQueue()
que.put(1)
que.put("perro")
que.put(False)
for i in range(4):
if not que.isempty():
print(que.get())
else:
print("Cola vacía")

3.3.1.1 POO: Propiedades

Variables de instancia
En general, una clase puede equiparse con dos tipos diferentes de datos para formar las
propiedades de una clase. Ya viste uno de ellos cuando estábamos estudiando pilas.

Este tipo de propiedad existe solo cuando se crea explícitamente y se agrega a un objeto.
Como ya sabes, esto se puede hacer durante la inicialización del objeto, realizada por el
constructor.

Además, se puede hacer en cualquier momento de la vida del objeto. Es importante


mencionar también que cualquier propiedad existente se puede eliminar en cualquier
momento.

Tal enfoque tiene algunas consecuencias importantes:

• Diferentes objetos de la misma clase pueden poseer diferentes conjuntos de


propiedades.
• Debe haber una manera de verificar con seguridad si un objeto específico posee
la propiedad que deseas utilizar (a menos que quieras generar 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.

Aquí hay un ejemplo:

class ExampleClass:
def __init__(self, val = 1):
self.first = val
def set_second(self, val):
self.second = val

example_object_1 = ExampleClass()
example_object_2 = ExampleClass(2)

example_object_2.set_second(3)

example_object_3 = ExampleClass(4)
example_object_3.third = 5

print(example_object_1.__dict__)
print(example_object_2.__dict__)
print(example_object_3.__dict__)

Se necesita una explicación adicional antes de entrar en más detalles. Echa un vistazo a
las últimas tres líneas del código.

Los objetos de Python, cuando se crean, están dotados de un pequeño conjunto de


propiedades y métodos predefinidos. Cada objeto los tiene, los quieras o no. Uno de ellos
es una variable llamada __dict__ (es un diccionario).

La variable contiene los nombres y valores de todas las propiedades (variables) que el
objeto contiene actualmente. Vamos a usarla para presentar de forma segura el
contenido de un objeto.

Vamos a sumergirnos en el código ahora:

• La clase llamada ExampleClass tiene un constructor, el cual crea


incondicionalmente una variable de instancia llamada first , 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 second .

• Hemos creado tres objetos de la clase ExampleClass , pero todas estas instancias
difieren:

o example_object_1 solo tiene una propiedad llamada first .

o example_object_2 tiene dos propiedades: first y second .


o example_object_3 ha sido enriquecido sobre la marcha con una propiedad
llamada third uera del código de la clase: esto es posible y totalmente
permisible.

La salida del programa muestra claramente que nuestras suposiciones son correctas:
aquí están:

{'first': 1}
{'second': 3, 'first': 2}
{'third': 5, 'first': 4}
salida

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.

3.3.1.2 POO: Propiedades

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 antepuesto dos guiones bajos ( __ ).

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:

{'_ExampleClass__first': 1}
{'_ExampleClass__first': 2, '_ExampleClass__second': 3}
{'_ExampleClass__first': 4, '__third': 5}
salida

¿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 __first se convierte en _ExampleClass__first .


El nombre ahora es completamente accesible desde fuera de la clase. Puedes ejecutar un
código como este:

print(example_object_1._ExampleClass__first)

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 la clase. En este
caso, se comportará como cualquier otra propiedad ordinaria.

class ExampleClass:
def __init__(self, val = 1):
self.__first = val

def set_second(self, val = 2):


self.__second = val

example_object_1 = ExampleClass()
example_object_2 = ExampleClass(2)

example_object_2.set_second(3)

example_object_3 = ExampleClass(4)
example_object_3.__third = 5

print(example_object_1.__dict__)
print(example_object_2.__dict__)
print(example_object_3.__dict__)

3.3.1.3 POO: Propiedades

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 de la clase; solo 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 ExampleClass:
counter = 0
def __init__(self, val = 1):
self.__first = val
ExampleClass.counter += 1

example_object_1 = ExampleClass()
example_object_2 = ExampleClass(2)
example_object_3 = ExampleClass(4)

print(example_object_1.__dict__, example_object_1.counter)
print(example_object_2.__dict__, example_object_2.counter)
print(example_object_3.__dict__, example_object_3.counter)

Observa:

• Hay una asignación en la primera linea de la definición de clase: establece la


variable denominada counter 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 provocará el siguiente resultado:

{'_ExampleClass__first': 1} 3
{'_ExampleClass__first': 2} 3
{'_ExampleClass__first': 4} 3
salida

Dos conclusiones importantes se pueden sacar del ejemplo:

• Las variables de clase no se muestran en el diccionario de un


objeto __dict__ (esto es natural ya que las variables de clase no son partes de un
objeto), pero siempre puedes intentar buscar en la variable del mismo nombre,
pero a nivel de clase, te mostraremos esto muy pronto.
• Una variable de clase siempre presenta el mismo valor en todas las instancias de
clase (objetos).

3.3.1.4 POO: Propiedades

Variables de clase: continuación


El cambiar el nombre de una variable de clase tiene los mismos efectos que aquellos con
los que ya está familiarizado.

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 ExampleClass:
__counter = 0
def __init__(self, val = 1):
self.__first = val
ExampleClass.__counter += 1

example_object_1 = ExampleClass()
example_object_2 = ExampleClass(2)
example_object_3 = ExampleClass(4)

print(example_object_1.__dict__, example_object_1._ExampleClass__counter)
print(example_object_2.__dict__, example_object_2._ExampleClass__counter)
print(example_object_3.__dict__, example_object_3._ExampleClass__counter)

3.3.1.5 POO: Propiedades

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:

1. Definimos una clase llamada ExampleClass .


2. La clase define una variable de clase llamada varia .
3. El constructor de la clase establece la variable con el valor del parámetro.
4. Nombrar la variable es el aspecto más importante del ejemplo porque:
o El cambiar la asignación a self.varia = val crearía una variable de instancia
con el mismo nombre que la de la clase.
o El cambiar la asignación a varia = val operaría en la variable local de un
método; (te recomendamos probar los dos casos anteriores; esto te facilitará
recordar la diferencia).
5. La primera línea del código fuera de la clase imprime el valor del
atributo ExampleClass.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 ExampleClass:
varia = 1
def __init__(self, val):
ExampleClass.varia = val

print(ExampleClass.__dict__)
example_object = ExampleClass(2)

print(ExampleClass.__dict__)
print(example_object.__dict__)

3.3.1.6 POO: Propiedades

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(example_object.b)
AttributeError: 'ExampleClass' object has no attribute 'b'
salida

Como puedes ver, acceder a un atributo de objeto (clase) no existente genera una
excepción AttributeError.

class ExampleClass:
def __init__(self, val):
if val % 2 != 0:
self.a = 1
else:
self.b = 1

example_object = ExampleClass(1)
print(example_object.a)
print(example_object.b)

3.3.1.7 POO: Propiedades

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

La función retorna True o False.

Así es como puedes utilizarla:

class ExampleClass:
def __init__(self, val):
if val % 2 != 0:
self.a = 1
else:
self.b = 1

example_object = ExampleClass(1)
print(example_object.a)

if hasattr(example_object, 'b'):
print(example_object.b)

class ExampleClass:
def __init__(self, val):
if val % 2 != 0:
self.a = 1
else:
self.b = 1
example_object = ExampleClass(1)
print(example_object.a)

try:
print(example_object.b)
except AttributeError:
pass

3.3.1.8 POO: Propiedades

Comprobando la existencia de un atributo: continuación


No olvides que la función hasattr() también puede operar en clases. Puedes
usarla 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 ExampleClass:
a = 1
def __init__(self):
self.b = 2

example_object = ExampleClass()

print(hasattr(example_object, 'b'))
print(hasattr(example_object, 'a'))
print(hasattr(ExampleClass, 'b'))
print(hasattr(ExampleClass, 'a'))

¿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 ExampleClass:
attr = 1

print(hasattr(ExampleClass, 'attr'))
print(hasattr(ExampleClass, 'prop'))

3.3.1.9 RESUMEN DE SECCIÓN

Puntos Clave

1. Una variable de instancia es una propiedad cuya existencia depende de la creación de


un objeto. Cada objeto puede tener un conjunto diferente de variables de instancia.

Además, se pueden agregar y quitar libremente de los objetos durante su vida útil. Todas
las variables de instancia de objeto se almacenan dentro de un diccionario dedicado
llamado __dict__ , contenido en cada objeto por separado.

2. Una variable de instancia puede ser privada cuando su nombre comienza con __ , pero
no olvides que dicha propiedad aún es accesible desde fuera de la clase usando
un nombre modificado construido como < codel>_ClassName__PrivatePropertyName.

3. Una variable de clase es una propiedad que existe exactamente en una copia y no
necesita ningún objeto creado para ser accesible. Estas variables no se muestran como
contenido de __dict__ .

Todas las variables de clase de una clase se almacenan dentro de un diccionario dedicado
llamado __dict__ , contenido en cada clase por separado.

4. Una función llamada hasattr() se puede utilizar para determinar si algún objeto o
clase contiene cierta propiedad especificada.

Por ejemplo:

class Sample:
gamma = 0 # Class variable.
def __init__(self):
self.alpha = 1 # Variable de instancia.
self.__delta = 3 # Variable de instancia privada.

obj = Sample()
obj.beta = 2 # Otra variable de instancia (que existe solo dentro de
la instancia "obj").
print(obj.__dict__)

El código da como salida:

{'alpha': 1, '_Sample__delta': 3, 'beta': 2}


salida

Ejercicio 1
¿Cuáles de las propiedades de la clase Python son variables de instancia y cuáles son
variables de clase? ¿Cuáles de ellos son privados?

class Python:
population = 1
victims = 0
def __init__(self):
self.length_ft = 3
self.__venomous = False

Revisar

population y victims son variables de clase, mientras que length y __venomous son
variables de instancia (esta última también es privada).

Ejercicio 2

Vas a negar la propiedad __venomous del objeto version_2 , ignorando el hecho de que
la propiedad es privada. ¿Cómo vas a hacer esto?

version_2 = Python()

Revisar

version_2._Python__venomous = not version_2._Python__venomous

Ejercicio 3

Escribe una expresión que compruebe si el objeto version_2 contiene una propiedad de
instancia denominada constrictor (¡si, constrictor!).

Revisar

hasattr(version_2, 'constrictor')

3.4.1.1 POO: Métodos

Métodos a detalle
Resumamos todos los hechos relacionados con el uso de métodos en las clases de
Python.

Como ya sabes, un método es una función que está dentro de una clase.

Existe 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
salida

Toma en cuenta la forma en que hemos creado el objeto, hemos tratado el nombre de la
clase como una función, y devuelve un objeto recién instanciado de la clase.

Si deseas que el método acepte parámetros distintos a self , debes:

• Colocarlos después de self en la definición del método.


• Pasarlos como argumentos durante la invocación sin especificar self .

Justo como aqui:

class Classy:
def method(self, par):
print("método:", par)

obj = Classy()
obj.method(1)
obj.method(2)
obj.method(3)

El código da como salida:

método: 1
método: 2
método: 3
salida
class Classy:
def method(self):
print("método")

obj = Classy()
obj.method()
3.4.1.2 POO: Métodos

Métodos a detalle: continuación


El parámetro self es usado para obtener acceso a la instancia del objeto y las variables
de clase.

El ejemplo muestra ambas formas de utilizar el parámetro self :

class Classy:
varia = 2
def method(self):
print(self.varia, self.var)

obj = Classy()
obj.var = 3
obj.method()

El código da como salida:

2 3
salida

El parámetro self también se usa para invocar otros métodos desde dentro de la clase.

Justo como aquí:

class Classy:
def other(self):
print("otro")

def method(self):
print("método")
self.other()

obj = Classy()
obj.method()

El código da como salida:

método
otro
salida
# Probar ejemplos aquí.

3.4.1.3 POO: Métodos


Métodos a detalle: continuación
Si se nombra un método de esta manera: __init__ , no será un método regular, será
un constructor.

Si una clase tiene un constructor, este se invoca automática e implícitamente cuando se


instancia el objeto de la clase.

El constructor:

• Esta obligado a tener el parámetro self (se configura automáticamente).


• Pudiera (pero no necesariamente) tener mas parámetros que solo self ; si esto
sucede, la forma en que se usa el nombre de la clase para crear el objeto debe
tener la definición __init__ .
• Se puede utilizar para configurar el objeto, es decir, inicializa adecuadamente su
estado interno, crea variables de instancia, crea instancias de cualquier otro
objeto si es necesario, etc.

Observa el código en el editor. El ejemplo muestra un constructor muy simple pero


funcional.

Ejecútalo. El código da como salida:

objeto
salida

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 Classy:
def __init__(self, value):
self.var = value

obj_1 = Classy("objeto")

print(obj_1.var)

3.4.1.4 POO: Métodos


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
salida

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 Classy:
def visible(self):
print("visible")

def __hidden(self):
print("oculto")

obj = Classy()
obj.visible()

try:
obj.__hidden()
except:
print("fallido")

obj._Classy__hidden()

El código da como salida:

visible
fallido
oculto
salida

Ejecuta el programa y pruébalo.

class Classy:
def __init__(self, value = None):
self.var = value
obj_1 = Classy("objeto")
obj_2 = Classy()

print(obj_1.var)
print(obj_2.var)

3.4.1.5 POO: Métodos

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 como esta propiedad trata con los métodos: mira el código en el editor.

Ejecútalo para ver que produce. Verifica el resultado.

Encuentra todos los métodos y atributos definidos. Localiza el contexto en el que existen:
dentro del objeto o dentro de la clase.

class Classy:
varia = 1
def __init__(self):
self.var = 2

def method(self):
pass

def __hidden(self):
pass

obj = Classy()

print(obj.__dict__)
print(Classy.__dict__)

3.4.1.6 POO: Métodos

La vida interna de clases y objetos: continuación


__dict__ es un diccionario. Otra propiedad incorporada que vale la pena mencionar es
una cadena llamada __name__ .
La propiedad contiene el nombre de la clase. No es nada emocionante, es solo una
cadena.

Nota: el atributo __name__ está ausente del objeto, existe solo dentro de las clases.

Si deseas encontrar la clase de un objeto en particular, puedes usar una función


llamada type() , la cual es capaz (entre otras cosas) de encontrar una clase que se haya
utilizado para crear instancias de cualquier objeto.

Observa el código en el editor, ejecútalo y compruébalo tu mismo.

La salida del código es:

Classy
Classy
salida

Nota: algo como esto

print(obj.__name__)

causará un error.

class Classy:
pass

print(Classy.__name__)
obj = Classy()
print(type(obj).__name__)

3.4.1.7 POO: Métodos

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.

La salida del código es:

__main__
__main__
salida

Como sabes, cualquier módulo llamado __main__ en realidad no es un módulo, sino es


el archivo actualmente en ejecución.
class Classy:
pass

print(Classy.__module__)
obj = Classy()
print(obj.__module__)

3.4.1.8 POO: Métodos

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 de 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 cómo funciona la
herencia.

Además, te mostraremos cómo usar este atributo cuando discutamos los aspectos
orientados a objetos de las excepciones.

Nota: solo las clases tienen este atributo, los objetos no.

Hemos definido una función llamada printBases() , diseñada para presentar


claramente el contenido de la tupla.

Observa el código en el editor. Ejecútalo. Su salida es:

( object )
( object )
( SuperOne SuperTwo )
salida

Nota: una clase sin superclases explícitas apunta a object (una clase de Python
predefinida) como su antecesor directo.

class SuperOne:
pass

class SuperTwo:
pass

class Sub(SuperOne, SuperTwo):


pass
def printBases(cls):
print('( ', end='')

for x in cls.__bases__:
print(x.__name__, end=' ')
print(')')

printBases(SuperOne)
printBases(SuperTwo)
printBases(Sub)

3.4.1.9 POO: Métodos

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.

3.4.1.10 POO: Métodos

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.

La función llamada incIntsI() toma 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 ninguna manera!

Así es como funciona:

• La línea 1: define una clase muy simple...


• Las líneas 3 a la 10: ... la llenan con algunos atributos.
• La línea 12: ¡esta es nuestra función!
• La línea 13: escanea el atributo __dict__ , buscando todos los nombres de
atributos.
• La línea 14: si un nombre comienza con i...
• La 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.
• La 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).
• La 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, 'integer': 4, 'b': 2, 'i': 3, 'z': 5, 'ireal': 3.5}


{'a': 1, 'integer': 5, 'b': 2, 'i': 4, 'z': 5, 'ireal': 3.5}
salida

¡Eso es todo!

class MyClass:
pass

obj = MyClass()
obj.a = 1
obj.b = 2
obj.i = 3
obj.ireal = 3.5
obj.integer = 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__)

3.4.1.11 RESUMEN DE SECCION

Puntos Clave
1. Un método es una función dentro de una clase. El primer (o único) parámetro de cada
método se suele llamar self , que está diseñado para identificar al objeto para el que se
invoca el método con el fin de acceder a las propiedades del objeto o invocar sus
métodos.

2. Si una clase contiene un constructor (un método llamado __init__ ), este no puede
devolver ningún valor y no se puede invocar directamente.

3. Todas las clases (pero no los objetos) contienen una propiedad llamada __name__ , que
almacena el nombre de la clase. Además, una propiedad llamada __module__ almacena
el nombre del módulo en el que se ha declarado la clase, mientras que la propiedad
llamada __bases__ es una tupla que contiene las superclases de una clase.

Por ejemplo:

class Sample:
def __init__(self):
self.name = Sample.__name__
def myself(self):
print("Mi nombre es " + self.name + " y vivo en " +
Sample.__module__)

obj = Sample()
obj.myself()

El código da como salida:

Mi nombre es Sample y vivo en __main__

salida
Ejercicio 1

La declaración de la clase Snake se muestra a continuación. Enriquece la clase con un


método llamado increment() , el cual incrementa en 1 la propiedad victims .

class Snake:
def __init__(self):
self.victims = 0

Revisar

class Snake:
def __init__(self):
self.victims = 0

def increment(self):
self.victims += 1

Ejercicio 2
Redefine el constructur de la clase Snake para que tenga un parámetro que inicialice el
campo victims con un valor pasado al objeto durante la construcción.

Revisar

class Snake:
def __init__(self, victims):
self.victims = victims

Ejercicio 3
¿Puedes predecir el resultado del siguiente código?
class Snake:
pass

class Python(Snake):
pass

print(Python.__name__, 'es una', Snake.__name__)


print(Python.__bases__[0].__name__, 'puede ser una', Python.__name__)

Revisar

Python es una Snake


Snake puede ser una Python

3.4.1.12 La clase Timer

LABORATORIO
Tiempo Estimado
30-60 minutos

Nivel de Dificultad
Fácil/Medio

Objetivos
• Mejorar las habilidades del estudiante para definir clases desde cero.
• Definir y usar variables de instancia.
• Definir y usar métodos.

Escenario
Necesitamos una clase capaz de contar segundos. ¿Fácil? No es tan fácil como podrías
pensar, ya que tendremos algunos requisitos específicos.

Léelos con atención, ya que la clase sobre la que escribes se utilizará para lanzar cohetes
en misiones internacionales a Marte. Es una gran responsabilidad. ¡Contamos contigo!

Tu clase se llamará Timer (temporizador en español). Su constructor acepta tres


argumentos que representan horas (un valor del rango [0..23]; usaremos tiempo
militar), minutos (del rango [0. .59]) y segundos (del rango [0..59]).

Cero es el valor predeterminado para todos los parámetros anteriores. No es necesario


realizar ninguna comprobación de validación.

La clase en sí debería proporcionar las siguientes facilidades:

• Los objetos de la clase deben ser "imprimibles", es decir, deben poder convertirse
implícitamente en cadenas de la siguiente forma: "hh:mm:ss", con ceros a la
izquierda agregados cuando cualquiera de los valores es menor que 10.
• La clase debe estar equipada con métodos sin parámetros
llamados next_second() y previous_second () , incrementando el tiempo
almacenado dentro de los objetos en +1/-1 segundos respectivamente.

Emplea las siguientes sugerencias:

• Todas las propiedades del objeto deben ser privadas.


• Considera escribir una función separada (¡no un método!) para formatear la
cadena con el tiempo.

Completa la plantilla que te proporcionamos en el editor. Ejecuta tu código y comprueba


si el resultado es el mismo que el nuestro.
Salida Esperada
23:59:59
00:00:00
23:59:59

class Timer:
def __init__( ??? ):
#
# Escribir código aquí.
#

def __str__(self):
#
# Escribir código aquí.
#

def next_second(self):
#
# Escribir código aquí.
#

def prev_second(self):
#
# Escribir código aquí.
#

timer = Timer(23, 59, 59)


print(timer)
timer.next_second()
print(timer)
timer.prev_second()
print(timer)

3.4.1.13 Días de la semana


LABORATORIO

Tiempo Estimado
30-60 minutos

Nivel de Dificultad
Fácil/Medio
Objetivos
• Mejorar las habilidades del estudiante para definir clases desde cero.
• Definir y usar variables de instancia.
• Definir y usar métodos.

Escenario
Tu tarea es implementar una clase llamada Weeker. Sí, tus ojos no te engañan, este
nombre proviene del hecho de que los objetos de esta clase podrán almacenar y
manipular los días de la semana.

El constructor de la clase acepta un argumento: una cadena. La cadena representa el


nombre del día de la semana y los únicos valores aceptables deben provenir del siguiente
conjunto:

Lun Mar Mie Jue Vie Sab Dom

Invocar al constructor con un argumento desde fuera de este conjunto debería generar la
excepción WeekDayError (defínela tu mismo; no te preocupes, pronto hablaremos sobre
la naturaleza objetiva de las excepciones). La clase debe proporcionar las siguientes
facilidades:

• Los objetos de la clase deben ser "imprimibles", es decir, deben poder convertirse
implícitamente en cadenas de la misma forma que los argumentos del
constructor.
• La clase debe estar equipada con métodos de un parámetro
llamados add_days(n) y subtract_days(n) , siendo n un número entero que
actualiza el día de la semana almacenado dentro del objeto mediante el número
de días indicado, hacia adelante o hacia atrás.
• Todas las propiedades del objeto deben ser privadas.

Completa la plantilla que te proporcionamos en el editor, ejecuta su código y verifica si tu


salida se ve igual que la nuestra.

Salida Esperada
Lun
Mar
Dom
Lo siento, no puedo atender tu solicitud.

class WeekDayError(Exception):
pass

class Weeker:
#
# Escribir código aquí.
#
def __init__(self, day):
#
# Escribir código aquí.
#

def __str__(self):
#
# Escribir código aquí.
#

def add_days(self, n):


#
# Escribir código aquí.
#

def subtract_days(self, n):


#
# Escribir código aquí.
#

try:
weekday = Weeker('Lun')
print(weekday)
weekday.add_days(15)
print(weekday)
weekday.subtract_days(23)
print(weekday)
weekday = Weeker('Lun')
except WeekDayError:
print("Lo siento, no puedo atender tu solicitud.")

3.4.1.14 Puntos en un plano

LABORATORIO

Tiempo Estimado
30-60 minutos

Nivel de Dificultad
Fácil/Medio
Objetivos
• Mejorar las habilidades del estudiante para definir clases desde cero.
• Definir y usar variables de instancia.
• Definir y usar métodos.

Escenario
Visitemos un lugar muy especial: un plano con el sistema de coordenadas cartesianas
(puedes obtener más información sobre este concepto
aquí: https://en.wikipedia.org/wiki/Cartesian_coordinate_system).

Cada punto ubicado en el plano puede describirse como un par de coordenadas


habitualmente llamadas x y y. Queremos que escribas una clase en Python que almacene
ambas coordenadas como números flotantes. Además, queremos que los objetos de esta
clase evalúen las distancias entre cualquiera de los dos puntos situados en el plano.

La tarea es bastante fácil si empleas la función denominada hypot() (disponible a través


del módulo math) que evalúa la longitud de la hipotenusa de un triángulo rectángulo
(más detalles aquí: https://en.wikipedia.org/wiki/Hypotenuse) y
aquí: https://docs.python.org/3.7/library/math.html#trigonometric-functions.

Así es como imaginamos la clase:

• Se llama Point .
• Su constructor acepta dos argumentos (x y y respectivamente), ambos por defecto
se igualan a cero.
• Todas las propiedades deben ser privadas.
• La clase contiene dos métodos sin parámetros llamados getx() y gety() , que
devuelven cada una de las dos coordenadas (las coordenadas se almacenan de
forma privada, por lo que no se puede acceder a ellas directamente desde el
objeto).
• La clase proporciona un método llamado distance_from_xy(x,y) , que calcula y
devuelve la distancia entre el punto almacenado dentro del objeto y el otro punto
dado en un par de números flotantes.
• La clase proporciona un método llamado distance_from_point(point) , que
calcula la distancia (como el método anterior), pero la ubicación del otro punto se
da como otro objeto de clase Point.

Completa la plantilla que te proporcionamos en el editor, ejecuta tu código y verifica si tu


salida se ve igual que la nuestra.

Salida esperada
1.4142135623730951
1.4142135623730951

import math
class Point:
def __init__(self, x=0.0, y=0.0):
#
# Escribir el código aquí.
#

def getx(self):
#
# Escribir el código aquí.
#

def gety(self):
#
# Escribir el código aquí.
#

def distance_from_xy(self, x, y):


#
# Escribir el código aquí.
#

def distance_from_point(self, point):


#
# Escribir el código aquí.
#

point1 = Point(0, 0)
point2 = Point(1, 1)
print(point1.distance_from_point(point2))
print(point2.distance_from_xy(2, 0))

3.4.1.15 Triángulo
LABORATORIO

Tiempo Estimado
30-60 minutos

Nivel de Dificultad
Fácil/Medio
Objetivos
• Mejorar las habilidades del estudiante para definir clases desde cero.
• Emplear composición.

Escenario
Ahora vamos a colocar la clase Point (ver Lab 3.4.1.14) dentro de otra clase. Además,
vamos a poner tres puntos en una clase, lo que nos permitirá definir un triángulo.¿Cómo
podemos hacerlo?

La nueva clase se llamará Triangle y esto es lo que queremos:

• El constructor acepta tres argumentos - todos ellos son objetos de la clase Point .
• Los puntos se almacenan dentro del objeto como una lista privada
• La clase proporciona un método sin parámetros llamado perimeter() , que
calcula el perímetro del triángulo descrito por los tres puntos; el perímetro es la
suma de todas las longitudes de los lados (lo mencionamos para que conste,
aunque estamos seguros de que tú mismo lo conoces perfectamente).

Completa la plantilla que te proporcionamos en el editor, ejecuta tu código y verifica si tu


salida se ve igual que la nuestra.

A continuación puedes copiar el código de la clase Point , el cual se utilizo en el


laboratorio anterior:

Revisar
class Point:
def __init__(self, x=0.0, y=0.0):
self.__x = x
self.__y = y

Salida esperada
3.414213562373095

import math

class Point:
#
# El código copiado del laboratorio anterior.
#

class Triangle:
def __init__(self, vertice1, vertice2, vertice3):
#
# Escribir el código aquí.
#

def perimeter(self):
#
# Escribir el código aquí.
#

triangle = Triangle(Point(0, 0), Point(1, 0), Point(0, 1))


print(triangle.perimeter())

3.5.1.1 Fundamentos de POO: Herencia

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__.Star object at 0x7f1074cc7c50>


salida

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 Star:
def __init__(self, name, galaxy):
self.name = name
self.galaxy = galaxy

sun = Star("Sol", "Vía Láctea")


print(sun)

3.5.1.2 Fundamentos de POO: Herencia


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.

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 Star:
def __init__(self, name, galaxy):
self.name = name
self.galaxy = galaxy

def __str__(self):
return self.name + ' en ' + self.galaxy

sun = Star("Sol", "Vía Láctea")


print(sun)

3.5.1.3 Fundamentos de POO: Herencia

Herencia: ¿por qué y cómo?


El término herencia es más antiguo que la
programación de computadoras, y
describe la práctica común de pasar
diferentes bienes de una persona a otra
después de la muerte de esa persona. El
término, cuando se relaciona con la
programación de computadoras, tiene un
significado completamente diferente.
Definamos el término para nuestros propósitos:

La herencia es una práctica común (en la programación de objetos) de pasar atributos y


métodos de la superclase (definida y existente) a una clase recién creada, llamada
subclase.

En otras palabras, la herencia es una forma de construir una nueva clase, no desde cero,
sino utilizando un repertorio de rasgos ya definido. La nueva clase hereda (y esta es la
clave) todo el equipamiento ya existente, pero puedes agregar algo nuevo si es necesario.

Gracias a eso, es posible construir clases más especializadas (más concretas) utilizando
algunos conjuntos de reglas y comportamientos generales predefinidos.

El factor más importante del proceso es la relación entre la superclase y todas sus
subclases (nota: si B es una subclase de A y C es una subclase de B, esto también significa
que C es una subclase de A, ya que la relación es totalmente transitiva).

Aquí se presenta un ejemplo muy simple de herencia de dos niveles:

class Vehicle:
pass

class LandVehicle(Vehicle):
pass

class TrackedVehicle(LandVehicle):
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 Vehicle es la superclase para clases LandVehicle y TrackedVehicle .


• La clase LandVehicle es una subclase de Vehicle y la superclase
de TrackedVehicle al mismo tiempo.
• La clase TrackedVehicle es una subclase tanto de Vehicle y LandVehicle .

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.

3.5.1.4 Fundamentos de POO: Herencia


Herencia: issubclass()
Python ofrece una función que es capaz de identificar una relación entre dos clases, y
aunque su diagnóstico no es complejo, puede verificar si una clase particular es una
subclase de cualquier otra clase.

Así es como se ve:

issubclass(ClassOne, ClassTwo)

La función devuelve True si ClassOne es una subclase de ClassTwo , 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
salida

Hagamos que el resultado sea más legible:

↓ es una subclase de → Vehicle LandVehicle TrackedVehicle

Vehicle True False False

LandVehicle True True False

TrackedVehicle True True True

Existe una observación importante que hacer: cada clase se considera una subclase de sí
misma.

class Vehicle:
pass

class LandVehicle(Vehicle):
pass
class TrackedVehicle(LandVehicle):
pass

for cls1 in [Vehicle, LandVehicle, TrackedVehicle]:


for cls2 in [Vehicle, LandVehicle, TrackedVehicle]:
print(issubclass(cls1, cls2), end="\t")
print()

3.5.1.5 Fundamentos de POO: Herencia

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 que receta se ha utilizado para prepararlo. ¿Por qué? Porque
deseas saber que esperar de él, por ejemplo, si contiene nueces o no, lo cual es
información crucial para ciertas personas.

Del mismo modo, puede ser crucial si el objeto tiene (o no tiene) ciertas características.
En otras palabras, si es un objeto de cierta clase o no.

Tal hecho podría ser detectado por la función llamada isinstance() :

isinstance(objectName, ClassName)

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 características 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
salida

Hagamos que el resultado sea más legible:

↓ es una instancia de → Vehicle LandVehicle TrackedVehicle

my_vehicle True False False

my_land_vehicle True True False

my_tracked_vehicle True True True

¿La tabla confirma nuestras expectativas?

class Vehicle:
pass

class LandVehicle(Vehicle):
pass

class TrackedVehicle(LandVehicle):
pass

my_vehicle = Vehicle()
my_land_vehicle = LandVehicle()
my_tracked_vehicle = TrackedVehicle()

for obj in [my_vehicle, my_land_vehicle, my_tracked_vehicle]:


for cls in [Vehicle, LandVehicle, TrackedVehicle]:
print(isinstance(obj, cls), end="\t")
print()

3.5.1.6 Fundamentos de POO: Herencia

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

object_one is object_two
El operador is verifica si dos variables, en este caso ( object_one y object_two ) 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.

El código imprime:

False
False
True
1 2 1
True False
salida

Los resultados prueban que object_1 y object_3 son en realidad los mismos objetos,
mientras que string_1 y string_2 no lo son, a pesar de que su contenido sea el mismo.

class SampleClass:
def __init__(self, val):
self.val = val

object_1 = SampleClass(0)
object_2 = SampleClass(2)
object_3 = object_1
object_3.val += 1

print(object_1 is object_2)
print(object_2 is object_3)
print(object_3 is object_1)
print(object_1.val, object_2.val, object_3.val)

string_1 = "Mary tenía un "


string_2 = "Mary tenía un corderito"
string_1 += "corderito"

print(string_1 == string_2, string_1 is string_2)

3.5.1.7 Fundamentos de POO: Herencia

Cómo Python encuentra propiedades y métodos


Ahora veremos como 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 name .
• La clase también define el método __str__() , lo que permite que la clase pueda
presentar su identidad en forma de texto.
• La clase se usa luego como base para crear una subclase llamada Sub . La
clase Sub define su propio constructor, que invoca el de la superclase. Toma nota
de como lo hemos hecho: Super.__init__(self, name) .
• 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.
salida

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, name):
self.name = name

def __str__(self):
return "Mi nombre es " + self.name + "."

class Sub(Super):
def __init__(self, name):
Super.__init__(self, name)

obj = Sub("Andy")

print(obj)

3.5.1.8 Fundamentos de POO: Herencia

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__(name)

La función super() crea un contexto en el que no tiene que (además, no debe) pasar el
argumento propio al método que se invoca; es por eso que es posible activar el
constructor de la superclase utilizando solo un argumento.

Nota: puedes usar este mecanismo no solo para invocar al constructor de la superclase,
pero también para obtener acceso a cualquiera de los recursos disponibles dentro de la
superclase.

class Super:
def __init__(self, name):
self.name = name

def __str__(self):
return "Mi nombre es " + self.name + "."

class Sub(Super):
def __init__(self, name):
super().__init__(name)

obj = Sub("Andy")

print(obj)

3.5.1.9 Fundamentos de POO: Herencia


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.

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
salida

# Probando propiedades: variables de clase.


class Super:
supVar = 1

class Sub(Super):
subVar = 2

obj = Sub()

print(obj.subVar)
print(obj.supVar)

3.5.1.10 Fundamentos de POO: Herencia

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.

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
salida

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)

3.5.1.11 Fundamentos de POO: Herencia

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

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.

class Level1:
variable_1 = 100
def __init__(self):
self.var_1 = 101

def fun_1(self):
return 102

class Level2(Level1):
variable_2 = 200
def __init__(self):
super().__init__()
self.var_2 = 201

def fun_2(self):
return 202

class Level3(Level2):
variable_3 = 300
def __init__(self):
super().__init__()
self.var_3 = 301

def fun_3(self):
return 302

obj = Level3()

print(obj.variable_1, obj.var_1, obj.fun_1())


print(obj.variable_2, obj.var_2, obj.fun_2())
print(obj.variable_3, obj.var_3, obj.fun_3())

3.5.1.12 Fundamentos de POO: Herencia


Cómo Python encuentra propiedades y métodos:
continuación
La herencia múltiple ocurre cuando una clase tiene más de una superclase.

Sintácticamente, dicha herencia se presenta como una lista de superclases separadas por
comas entre paréntesis después del nombre de la nueva clase, al igual que aquí:

class SuperA:
var_a = 10
def fun_a(self):
return 11

class SuperB:
var_b = 20
def fun_b(self):
return 21

class Sub(SuperA, SuperB):


pass

obj = Sub()

print(obj.var_a, obj.fun_a())
print(obj.var_b, obj.fun_b())

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
salida

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:
var_a = 10
def fun_a(self):
return 11

class SuperB:
var_b = 20
def fun_b(self):
return 21

class Sub(SuperA, SuperB):


pass

obj = Sub()

print(obj.var_a, obj.fun_a())
print(obj.var_b, obj.fun_b())

3.5.1.13 Fundamentos de POO: Herencia

Cómo Python encuentra propiedades y métodos:


continuación
Analicemos el ejemplo en el editor.

Tanto la clase, Level1 y Level2 definen un método llamado fun() y una propiedad
llamada var . ¿Significará esto el objeto de la clase Level3 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
salida

Como puedes ver, la variable de clase var y el método fun() de la clase Level2 anula
las entidades de los mismos nombres derivados de la clase Level1 .

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.

¿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 Level1:
var = 100
def fun(self):
return 101
class Level2(Level1):
var = 200
def fun(self):
return 201

class Level3(Level2):
pass

obj = Level3()

print(obj.var, obj.fun())

3.5.1.14 Fundamentos de POO: Herencia

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 Left y Right ((estos nombres
están destinados a ser significativos).

No hay duda de que la variable de clase var_right proviene de la clase Right ,


y var_left proviene de la clase Left 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á invocada desde Left o
desde Right ? Ejecutemos el programa: la salida será:

L LL RR Left
salida

Esto prueba que ambos casos poco claros tienen una solución dentro de la clase Left .
¿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(Left, Right): con: class Sub(Right, Left): , luego ejecuta
el programa nuevamente y observa qué sucede.
¿Qué ves ahora? Vemos:

R LL RR Right
salida

¿Ves lo mismo o algo diferente?

class Left:
var = "L"
var_left = "LL"
def fun(self):
return "Left"

class Right:
var = "R"
var_right = "RR"
def fun(self):
return "Right"

class Sub(Left, Right):


pass

obj = Sub()

print(obj.var, obj.var_left, obj.var_right, obj.fun())

3.5.1.15 Fundamentos de POO: Herencia

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 cual de ellas debe ubicarse en la parte
superior y cual debe ubicarse en la parte inferior de la jerarquía, debes analizar
cuidadosamente el problema, pero antes de mostrarte como hacerlo (y como no hacerlo),
queremos resaltar un efecto interesante. No es nada extraordinario (es solo una
consecuencia de las reglas generales presentadas anteriormente), pero recordarlo puede
ser clave para comprender como 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 One y Two , se entiende que Two es derivada de One .
Nada especial. Sin embargo, algo es notable: el método do_it() .
• El método do_it() está definido dos veces: originalmente dentro
de One posteriormente dentro de Two . La esencia del ejemplo radica en el hecho
de que es invocado solo una vez dentro de One .

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 doanything() del objeto
llamado one 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 ejecutará el
método do_it() en la forma existente dentro de la clase Two , independientemente del
hecho de que la invocación se lleva a cabo dentro de la clase One .

En efecto, el código genera el siguiente resultado:

do_it from One


do_it from Two
salida

Nota: la situación en la cual la subclase puede modificar el comportamiento de su


superclase (como en el ejemplo) se llama poliformismo. 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 como usar el poliformismo para extender la flexibilidad de la clase.

class One:
def do_it(self):
print("do_it de One")

def doanything(self):
self.do_it()

class Two(One):
def do_it(self):
print("do_it de Two")

one = One()
two = Two()
one.doanything()
two.doanything()

3.5.1.16 Fundamentos de POO: Herencia

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_track() el cual se implementará más
tarde).
• Un vehículo con ruedas gira cuando sus ruedas delanteras giran (esto lo hace el
método turn_front_wheels() ).
• El método turn() utiliza el método adecuado para cada vehículo en particular.

¿Puedes detectar el error del código?

Los métodos turn() son muy similares como para dejarlos en esta forma.

Vamos a reconstruir el código: vamos a presentar una superclase para reunir todos los
aspectos similares de los vehículos, trasladando todos los detalles a las subclases.

import time

class TrackedVehicle:
def control_track(left, stop):
pass

def turn(left):
control_track(left, True)
time.sleep(0.25)
control_track(left, False)
class WheeledVehicle:
def turn_front_wheels(left, on):
pass

def turn(left):
turn_front_wheels(left, True)
time.sleep(0.25)
turn_front_wheels(left, False)

3.5.1.17 Fundamentos de POO: Herencia

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 Vehicle , la cual utiliza el método turn() para
implementar un esquema para poder girar, mientras que el giro en si es realizado
por change_direction() ; 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 TrackedVehicle (nota: es derivada de la
clase Vehicle ) la cual instancia el método change_direction() utilizando el
método denominado control_track() .
• Respectivamente, la subclase llamada WheeledVehicle hace lo mismo, pero usa
el método turn_front_wheels() 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 turn() , lo cual se puede hacer en un solo lugar, ya que todos los vehículos lo
obedecerán.

Así es como el el poliformismo ayuda al desarrollador a mantener el código limpio y


consistente.

import time

class Vehicle:
def change_direction(left, on):
pass

def turn(left):
change_direction(left, True)
time.sleep(0.25)
change_direction(left, False)
class TrackedVehicle(Vehicle):
def control_track(left, stop):
pass

def change_direction(left, on):


control_track(left, on)

class WheeledVehicle(Vehicle):
def turn_front_wheels(left, on):
pass

def change_direction(left, on):


turn_front_wheels(left, on)

3.5.1.18 Fundamentos de POO: Herencia

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


La herencia no es la única forma de construir clases adaptables. Puedes lograr los
mismos objetivos (no siempre, pero muy a menudo) utilizando una técnica llamada
composición.

La composición es el proceso de componer un objeto usando otros objetos diferentes.


Los objetos utilizados en la composición entregan un conjunto de rasgos deseados
(propiedades y/o métodos), podemos decir que actúan como bloques utilizados para
construir una estructura más complicada.

Puede decirse que:

• La herencia extiende las capacidades de una clase agregando nuevos


componentes y modificando los existentes; en otras palabras, la receta completa
está contenida dentro de la clase misma y todos sus ancestros; el objeto toma
todas las pertenencias de la clase y las usa.
• La composición proyecta una clase como contenedor capaz de almacenar y usar
otros objetos (derivados de otras clases) donde cada uno de los objetos
implementa una parte del comportamiento de una clase.

Permítenos ilustrar la diferencia usando los vehículos previamente definidos. El enfoque


anterior nos condujo a una jerarquía de clases en la que la clase más alta conocía las
reglas generales utilizadas para girar el vehículo, pero no sabía cómo controlar los
componentes apropiados (ruedas o pistas).

Las subclases implementaron esta capacidad mediante la introducción de mecanismos


especializados. Hagamos (casi) lo mismo, pero usando composición. La clase, como en el
ejemplo anterior, sabe cómo girar el vehículo, pero el giro real lo realiza un objeto
especializado almacenado en una propiedad llamada controlador . El controlador es
capaz de controlar el vehículo manipulando las partes relevantes del vehículo.

Echa un vistazo al editor: así es como podría verse.

Existen dos clases llamadas Tracks y Wheels , ellas saben como controlar la dirección del
vehículo. También hay una clase llamada Vehicle 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 Vehicle .

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


pistas: True False
tracks: False True
tracks: False False
salida

import time

class Tracks:
def change_direction(self, left, on):
print("pistas: ", left, on)

class Wheels:
def change_direction(self, left, on):
print("ruedas: ", left, on)

class Vehicle:
def __init__(self, controller):
self.controller = controller

def turn(self, left):


self.controller.change_direction(left, True)
time.sleep(0.25)
self.controller.change_direction(left, False)

wheeled = Vehicle(Wheels())
tracked = Vehicle(Tracks())

wheeled.turn(True)
tracked.turn(False)
3.5.1.19 Fundamentos de POO: Herencia

Herencia simple frente a herencia múltiple


Como ya sabes, no hay obstáculos para usar la herencia múltiple en Python. Puedes
derivar cualquier clase nueva de más de una clase definida previamente.

Solo hay un "pero". El hecho de que puedas hacerlo no significa que tengas que hacerlo.

No olvides que:

• Una sola clase de herencia siempre es más simple, segura y fácil de entender y
mantener.
• La herencia múltiple siempre es arriesgada, ya que tienes muchas más
oportunidades de cometer un error al identificar estas partes de las superclases
que influirán efectivamente en la nueva clase.
• La herencia múltiple puede hacer que la anulación sea extremadamente difícil;
además, el emplear la función super() se vuelve ambiguo.
• La herencia múltiple viola el principio de responsabilidad única (mas detalles
aquí: https://en.wikipedia.org/wiki/Single_responsibility_principle) ya que forma
una nueva clase de dos (o más) clases que no saben nada una de la otra.
• Sugerimos encarecidamente la herencia múltiple como la última de todas las
posibles soluciones: si realmente necesitas las diferentes funcionalidades que
ofrecen las diferentes clases, la composición puede ser una mejor alternativa.

3.5.1.20 Fundamentos de POO: MRO

¿Qué es el Orden de Resolución de Métodos (MRO) y por


qué no todas las herencias tienen sentido?
MRO, en general, es una forma (puedes llamarlo una estrategia) en la que un lenguaje de
programación en particular escanea la parte superior de la jerarquía de una clase para
encontrar el método que necesita actualmente. Vale la pena enfatizar que los diferentes
lenguajes usan MROs levemente (o incluso completamente) diferentes. Python es único
en este aspecto y sus costumbres son un poco específicas.

Te mostraremos cómo funciona el MRO de Python en dos casos peculiares que son
ejemplos claros de problemas que pueden ocurrir cuando intentas usar la herencia
múltiple de manera demasiado imprudente. Comencemos con un fragmento que
inicialmente puede parecer simple. Mira lo que te hemos preparado en el editor.

Estamos seguros de que si analizas el fragmento tu mismo, no verás ninguna anomalía


en él. Sí, tienes toda la razón: parece claro y simple, y no genera preocupaciones. Si
ejecutas el código, producirá el siguiente resultado predecible:

bottom
middle
top
salida

Sin sorpresas hasta ahora. Hagamos un pequeño cambio en este código. Echa un vistazo:

class Top:
def m_top(self):
print("top")

class Middle(Top):
def m_middle(self):
print("middle")

class Bottom(Middle, Top):


def m_bottom(self):
print("bottom")

object = Bottom()
object.m_bottom()
object.m_middle()
object.m_top()

¿Puedes ver la diferencia? Está escondida en esta línea:

class Bottom(Middle, Top):

De esta manera exótica, hemos convertido un código muy simple con una clara ruta de
herencia única en un misterioso acertijo de herencia múltiple. "¿Es válido?" Te puedes
preguntar. Sí lo es. "¿Cómo es eso posible?" te preguntas, esperamos que realmente
sientas la necesidad de hacer esta pregunta.

Como puedes ver, el orden en el que se enumeran las dos superclases entre paréntesis
cumple con la estructura del código: la clase Middle precede a la clase Top , justo como
en la ruta de herencia real.

A pesar de su rareza, la muestra es correcta y funciona como se esperaba, pero debe


indicarse que esta notación no aporta ninguna funcionalidad nueva ni significado
adicional.

Modifiquemos el código una vez más; ahora intercambiaremos ambos nombres de


superclase en la definición de clase Bottom . Así es como se ve el fragmento de código
ahora:

class Top:
def m_top(self):
print("top")
class Middle(Top):
def m_middle(self):
print("middle")

class Bottom(Top, Middle):


def m_bottom(self):
print("bottom")

object = Bottom()
object.m_bottom()
object.m_middle()
object.m_top()

Para anticiparnos a tu pregunta, diremos que esta enmienda ha estropeado el código y ya


no se ejecutará. Qué pena. El orden que intentamos forzar (Top, Middle) es incompatible
con la ruta de herencia que se deriva de la estructura del código. A Python no le gustará.
Esto es lo que veremos:

TypeError: Cannot create a consistent method resolution order (MRO)


for bases Top, Middle
salida

Creemos que el mensaje habla por sí solo. El MRO de Python no se puede doblar ni violar,
no solo porque esa es la forma en que funciona Python, sino también porque es una
regla que debes obedecer.

class Top:
def m_top(self):
print("top")

class Middle(Top):
def m_middle(self):
print("middle")

class Bottom(Middle):
def m_bottom(self):
print("bottom")

object = Bottom()
object.m_bottom()
object.m_middle()
object.m_top()
3.5.1.21 Fundamentos de POO: MRO

El Problema del Diamante


El segundo ejemplo del espectro de problemas que posiblemente pueden surgir de la
herencia múltiple está ilustrado por un problema clásico llamado problema del diamante.
El nombre refleja la forma del diagrama de herencia; observa la imagen:

• Existe la superclase superior llamada A.


• Existen dos subclases derivadas de A: B y C.
• 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 ahí?

Echa un vistazo al código en el editor. La misma estructura, pero expresada en Python.

Algunos lenguajes de programación no permiten la herencia múltiple en absoluto y, como


consecuencia, no te permitirán construir un diamante; este es el camino que Java y C#
han elegido seguir desde sus orígenes.

Python, sin embargo, ha elegido una ruta diferente: permite la herencia múltiple y no le
importa si escribe y ejecuta código como el del editor. Pero no te olvides del MRO:
siempre está a cargo.

Reconstruyamos nuestro ejemplo de la página anterior para hacerlo más parecido a un


diamante, como se muestra a continuación:

class Top:
def m_top(self):
print("top")

class Middle_Left(Top):
def m_middle(self):
print("middle_left")
class Middle_Right(Top):
def m_middle(self):
print("middle_right")

class Bottom(Middle_Left, Middle_Right):


def m_bottom(self):
print("bottom")

object = Bottom()
object.m_bottom()
object.m_middle()
object.m_top()

Nota: ambas clases Middle definen un método con el mismo nombre: m_middle() .

Introduce una pequeña incertidumbre en nuestra muestra, aunque estamos


absolutamente seguros de que puedes responder la siguiente pregunta clave: ¿cuál de
los dos métodos m_middle() se invocará realmente cuando la siguiente línea se ejecute?

Object.m_middle()

En otras palabras, qué verás en la pantalla: middle_left o middle_right ?

No es necesario que te apresures, ¡piénselo dos veces y toma en cuenta el MRO de


Python!

¿Estás listo?

Sí, tienes razón. La invocación activará el método m_middle() , que proviene de la


clase Middle_Left . La explicación es simple: la clase aparece antes de Middle_Right en
la lista de herencia de la clase Bottom . Si deseas asegurarte de que no haya dudas al
respecto, intenta intercambiar estas dos clases en la lista y verifica los resultados.

Si deseas experimentar algunas impresiones más profundas sobre la herencia múltiple y


las piedras preciosas, intenta modificar nuestro fragmento y equipar la clase Upper con
otro espécimen del método m_middle () e investigua su comportamiento
detenidamente.

Como puedes ver, los diamantes pueden traer algunos problemas a tu vida, tanto los
reales como los que ofrece Python.

class A:
pass

class B(A):
pass
class C(A):
pass

class D(B, C):


pass

d = D()

3.5.1.22 RESUMEN DE SECCIÓN 1/2

Puntos Clave
1. Un método llamado __str__() es responsable de convertir el contenido de un objeto
en una cadena (más o menos) legible. Puedes redefinirlo si deseas que tu objeto pueda
presentarse de una forma más elegante. Por ejemplo:

class Mouse:
def __init__(self, name):
self.my_name = name

def __str__(self):
return self.my_name

the_mouse = Mouse('mickey')
print(the_mouse) # Imprime "mickey".
2. Una función llamada issubclass(Class_1, Class_2) es capaz de determinar
si Class_1 es una subclase de Class_2 . Por ejemplo:

class Mouse:
pass

class LabMouse(Mouse):
pass

print(issubclass(Mouse, LabMouse), issubclass(LabMouse, Mouse)) #


Imprime "False True"

3. Una función llamada isinstance(Object, Class) comprueba si un objeto proviene


de una clase indicada. Por ejemplo:

class Mouse:
pass

class LabMouse(Mouse):
pass
mickey = Mouse()
print(isinstance(mickey, Mouse), isinstance(mickey, LabMouse)) #
Imprime "True False".

4. Un operador llamado is comprueba si dos variables hacen referencia al mismo objeto.


Por ejemplo:

class Mouse:
pass
mickey = Mouse()
minnie = Mouse()
cloned_mickey = mickey

# Imprime "False True".


print(mickey is minnie, mickey is cloned_mickey)

5. Una función sin parámetros llamada super() retorna la referencia a la superclase más
cercana de la clase. Por ejemplo:

class Mouse:
def __str__(self):
return "Mouse"

class LabMouse(Mouse):
def __str__(self):
return "Laboratory " + super().__str__()

doctor_mouse = LabMouse();
print(doctor_mouse) # Imprime "Laboratory Mouse".

6. Los métodos, así como las variables de instancia y de clase definidas en una superclase
son heredados automáticamente por sus subclases. Por ejemplo:

class Mouse:
Population = 0
def __init__(self, name):
Mouse.Population += 1
self.name = name

def __str__(self):
return "Hola, mi nombre es " + self.name

class LabMouse(Mouse):
pass

professor_mouse = LabMouse("Profesor Mouse")

# Imprime "Hola, mi nombre es Profesor Mouse 1"


print(professor_mouse, Mouse.Population)
7. Para encontrar cualquier propiedad de objeto/clase, Python la busca dentro:
• Del objeto mismo.
• Todas las clases involucradas en la línea de herencia del objeto de abajo hacia
arriba.
• Si existe más de una clase en una ruta de herencia en particular, Python las
escanea de izquierda a derecha.
• Si lo mencionado anteriormente falla, la excepción AttributeError es generada.

8. Si alguna de las subclases define un método, variable de clase o variable de instancia


del mismo nombre que existe en la superclase, el nuevo nombre anula cualquiera de las
instancias anteriores del nombre. Por ejemplo:

class Mouse:
def __init__(self, name):
self.name = name

def __str__(self):
return "Mi nombre es " + self.name

class AncientMouse(Mouse):
def __str__(self):
return "Meum nomen est " + self.name

mus = AncientMouse("Caesar") # Imprime "Meum nomen est Caesar"


print(mus)

3.5.1.23 RESUMEN DE SECCIÓN 2/2

Ejercicios
Escenario

El siguiente fragmento de código se ha ejecutado con éxito:

class Dog:
kennel = 0
def __init__(self, breed):
self.breed = breed
Dog.kennel += 1
def __str__(self):
return self.breed + " dice: ¡Guau!"

class SheepDog(Dog):
def __str__(self):
return super().__str__() + " ¡No huyas, corderito!"

class GuardDog(Dog):
def __str__(self):
return super().__str__() + " ¡Quédese donde está, intruso!"

rocky = SheepDog("Collie")
luna = GuardDog("Dobermann")

Ahora responde las preguntas de los ejercicios 1-4.

Ejercicio 1

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

print(rocky)
print(luna)

Revisar
Collie dice: ¡Guau! ¡No huyas, corderito!
Dobermann dice: ¡Guau! ¡Quédese donde está, intruso!

Ejercicio 2

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

print(issubclass(SheepDog, Dog), issubclass(SheepDog, GuardDog))


print(isinstance(rocky, GuardDog), isinstance(luna, GuardDog))

Revisar
True False
False True

Ejercicio 3

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

print(luna is luna, rocky is luna)


print(rocky.kennel)

Revisar
True False
2

Ejercicio 4

Define una subclase de SheepDog llamada LowlandDog , y equipala con un


método __str__() que anule un método heredado del mismo nombre. El nuevo
método __str__() debe retornar la cadena "¡Guau! ¡No me gustan las montañas!".

Revisar
class LowlandDog(SheepDog):
def __str__(self):
return Dog.__str__(self) + " ¡No me gustan las montañas"

3.6.1.1 Excepciones una vez más

Más acerca de 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 un bloque adicional que se


puede colocar dentro (o más bien, directamente detrás) del bloque try-except: es la parte
del código que comienza con else , justo como el ejemplo en el editor.

Un código etiquetado de esta manera se ejecuta cuando (y solo cuando) no se ha


generado ninguna excepción dentro de la parte del try: . Podemos decir que este bloque
se ejecuta después del try: , ya sea el que comienza con except (no olvides que puede
haber más de un bloque de este tipo) o la que comienza con else .

Nota: el bloque else: debe ubicarse después del último bloque except .

El código de ejemplo produce el siguiente resultado:

Todo salió bien


0.5
División fallida
None
salida

def reciprocal(n):
try:
n = 1 / n
except ZeroDivisionError:
print("División fallida")
return None
else:
print("Todo salió bien")
return n

print(reciprocal(2))
print(reciprocal(0))
3.6.1.2 Excepciones una vez más

Más acerca de excepciones


El try-except se puede extender de una manera más: agregando una parte encabezada
por la palabra clave reservada finally (debe ser el último bloque del código diseñado
para manejar excepciones).

Nota: estas dos variantes ( else y finally ) no son dependientes entre si, y pueden
coexistir u ocurrir de manera independiente.

El bloque finally siempre se ejecuta (finaliza la ejecución del bloque try-except, de ahí
su nombre), sin importar lo que sucedió antes, incluso cuando se genera una excepción,
sin importar si esta se ha manejado o no.

Observa el código en el editor. Su salida es:

Todo salió bien


Es momento de decir adiós
0.5
División fallida
Es momento de decir adiós
None
salida

def reciprocal(n):
try:
n = 1 / n
except ZeroDivisionError:
print("División fallida")
n = None
else:
print("Todo salió bien")
finally:
print("Es momento de decir adiós")
return n

print(reciprocal(2))
print(reciprocal(0))

3.6.1.3 Excepciones una vez más


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 el bloque "except" que está preparado
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 sí mismo.

try:
i = int("¡Hola!")
except Exception as e:
print(e)
print(e.__str__())

3.6.1.4 Excepciones una vez más

Las excepciones son clases


Todas las excepciones integradas de Python forman una jerarquía de clases. Si lo deseas,
puedes extenderlo sin problema.

Todas las excepciones integradas de Python forman una jerarquía de clases. Si lo deseas,
puedes extenderlo sin problema.
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 print_exception_tree() 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 excepciones de Python es la
clase BaseException (es una superclase de todas las demás excepciones).

Para cada una de las clases encontradas, se realiza el mismo conjunto de operaciones:

• Imprimir su nombre, tomado de la propiedad __name__ .


• Iterar a través de la lista de subclases provistas por el
método __subclasses__() , e invocar recursivamente la
función printExcTree() , incrementando el nivel de anidación respectivamente.

Toma en cuenta como hemos dibujado las ramas. La impresión no está ordenada de
alguna manera: si deseas un desafío, puedes intentar ordenarla tú mismo. Además, hay
algunas imprecisiones sutiles en la forma en que se presentan algunas ramas. Eso también
se puede arreglar, si lo deseas.

Así es como se ve:

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

def print_exception_tree(thisclass, nest = 0):


if nest > 1:
print(" |" * (nest - 1), end="")
if nest > 0:
print(" +---", end="")

print(thisclass.__name__)

for subclass in thisclass.__subclasses__():


print_exception_tree(subclass, nest + 1)

print_exception_tree(BaseException)

3.6.1.5 Excepciones una vez más

Anatomía detallada de las excepciones


Echemos un vistazo más de cerca al objeto de la excepción, ya que hay algunos
elementos realmente interesantes aquí (volveremos al tema pronto cuando
consideremos las técnicas base de entrada y salida de Python, ya que su subsistema de
excepción extiende un poco estos objetos).

La clase BaseException introduce una propiedad llamada args . Es una tupla diseñada
para reunir todos los argumentos pasados al constructor de la clase. Está vacío si la
construcción se ha invocado sin ningún argumento, o solo contiene un elemento cuando
el constructor recibe un argumento (no se considera el argumento self aquí), y así
sucesivamente.

Hemos preparado una función simple para imprimir la propiedad args de una manera
elegante, puedes ver la función en el editor.

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 generada 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 después 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')
salida

def print_args(args):
lng = len(args)
if lng == 0:
print("")
elif lng == 1:
print(args[0])
else:
print(str(args))

try:
raise Exception
except Exception as e:
print(e, e.__str__(), sep=' : ' ,end=' : ')
print_args(e.args)

try:
raise Exception("mi excepción")
except Exception as e:
print(e, e.__str__(), sep=' : ', end=' : ')
print_args(e.args)

try:
raise Exception("mi", "excepción")
except Exception as e:
print(e, e.__str__(), sep=' : ', end=' : ')
print_args(e.args)

3.6.1.6 Excepciones una vez más

Cómo crear tu propia excepción


La jerarquía de excepciones no está cerrada ni terminada, y siempre puedes ampliarla si
deseas o necesitas crear tu propio mundo poblado con tus propias excepciones.

Puede ser útil cuando se crea un módulo complejo que detecta errores y genera
excepciones, y deseas que las excepciones se distingan fácilmente de cualquier otra de
Python.

Esto se puede hacer al definir tus propias excepciones como subclases derivadas de las
predefinidas.

Nota: si deseas crear una excepción que se utilizará como un caso especializado de
cualquier excepción incorporada, derivala solo de esta. Si deseas construir tu propia
jerarquía, y no quieres que esté estrechamente conectada al árbol de excepciones de
Python, derivala de cualquiera de las clases de excepción principales, tal como: Exception.

Imagina que has creado una aritmética completamente nueva, regida por sus propias
leyes y teoremas. Está claro que la división también se ha redefinido, y tiene que
comportarse de una manera diferente a la división de rutina. También está claro que esta
nueva división debería plantear su propia excepción, diferente de la
incorporada ZeroDivisionError, pero es razonable suponer que, en algunas
circunstancias, tu (o el usuario de tu aritmética) pueden tratar todas las divisiones entre
cero de la misma manera.

Demandas como estas pueden cumplirse en la forma presentada en el editor. Mira el


código y analicémoslo:

• Hemos definido nuestra propia excepción, llamada MyZeroDivisionError ,


derivada de la incorporada ZeroDivisionError . Como puedes ver, hemos
decidido no agregar ningún componente nuevo a la clase.

En efecto, una excepción de esta clase puede ser, dependiendo del punto de vista
deseado, tratada como una simple excepción ZeroDivisionError, o puede ser
considerada por separado.

• La función do_the_division() genera 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 un bloque except (la más general), las
dos últimas invocan dos bloques diferentes, capaces de distinguir las excepciones
(no lo olvides: el orden de los bloques hace una diferencia fundamental).

class MyZeroDivisionError(ZeroDivisionError):
pass

def do_the_division(mine):
if mine:
raise MyZeroDivisionError("peores noticias")
else:
raise ZeroDivisionError("malas noticias")

for mode in [False, True]:


try:
do_the_division(mode)
except ZeroDivisionError:
print('División entre cero')

for mode in [False, True]:


try:
do_the_division(mode)
except MyZeroDivisionError:
print('Mi división entre cero')
except ZeroDivisionError:
print('División entre cero original')
3.6.1.7 Excepciones una vez más

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, message):
Exception.__init__(self, message)
self.pizza = pizza

Nota: vamos a recopilar más información específica aquí de lo que recopila


una Excepción regular, entonces nuestro constructor tomará dos argumentos:

• Uno que especifica una pizza como tema del proceso.


• Otro que contiene una descripción más o menos precisa del problema.

Como puedes ver, pasamos el segundo parámetro al constructor de la superclase y


guardamos el primero dentro de nuestra propiedad.

Un problema más específico (como un exceso de queso) puede requerir una excepción
más específica. Es posible derivar la nueva clase de la ya definida PizzaError , como
hemos hecho aquí:

class TooMuchCheeseError(PizzaError):
def __init__(self, pizza, cheese, message):
PizzaError._init__(self, pizza, message)
self.cheese = cheese

La excepción TooMuchCheeseError necesita más información que la excepción


regular PizzaError , así que lo agregamos al constructor, el nombre cheese es entonces
almacenado para su posterior procesamiento.

class PizzaError(Exception):
def __init__(self, pizza, message):
Exception.__init__(self, message)
self.pizza = pizza
class TooMuchCheeseError(PizzaError):
def __init__(self, pizza, cheese, message):
PizzaError._init__(self, pizza, message)
self.cheese = cheese

3.6.1.8 Excepciones una vez más

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 generada dentro de la función make_pizza() 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 el bloque que comienza con except TooMuchCheeseError hará que


todas las excepciones que aparecen se clasifiquen como PizzaError .
• El remover el bloque que comienza con except PizzaError provocará que la
excepción TooMuchCheeseError no pueda ser manejada, y hará que el programa
finalice.

La solución anterior, aunque elegante y eficiente, tiene una debilidad importante. Debido
a la manera algo fácil de declarar los constructores, las nuevas excepciones no se pueden
usar tal cual, sin una lista completa de los argumentos requeridos.

Eliminaremos esta debilidad estableciendo valores predeterminados para todos los


parámetros del constructor. Observa:

class PizzaError(Exception):
def __init__(self, pizza='desconocida', message=''):
Exception.__init__(self, message)
self.pizza = pizza

class TooMuchCheeseError(PizzaError):
def __init__(self, pizza='desconocida', cheese='>100', message=''):
PizzaError.__init__(self, pizza, message)
self.cheese = cheese

def make_pizza(pizza, cheese):


if pizza not in ['margherita', 'capricciosa', 'calzone']:
raise PizzaError
if cheese > 100:
raise TooMuchCheeseError
print("¡Pizza lista!")

for (pz, ch) in [('calzone', 0), ('margherita', 110), ('mafia', 20)]:


try:
make_pizza(pz, ch)
except TooMuchCheeseError as tmce:
print(tmce, ':', tmce.cheese)
except PizzaError as pe:
print(pe, ':', pe.pizza)

Ahora, si las circunstancias lo permiten, es posible usar únicamente los nombres de clase.

class PizzaError(Exception):
def __init__(self, pizza, message):
Exception.__init__(self, message)
self.pizza = pizza

class TooMuchCheeseError(PizzaError):
def __init__(self, pizza, cheese, message):
PizzaError.__init__(self, pizza, message)
self.cheese = cheese

def make_pizza(pizza, cheese):


if pizza not in ['margherita', 'capricciosa', 'calzone']:
raise PizzaError(pizza, "no hay tal pizza en el menú")
if cheese > 100:
raise TooMuchCheeseError(pizza, cheese, "demasiado queso")
print("¡Pizza lista!")

for (pz, ch) in [('calzone', 0), ('margherita', 110), ('mafia', 20)]:


try:
make_pizza(pz, ch)
except TooMuchCheeseError as tmce:
print(tmce, ':', tmce.cheese)
except PizzaError as pe:
print(pe, ':', pe.pizza)

3.6.1.9 RESUMEN DE SECCIÓN

Puntos Clave
1. El bloque else: de la sentencia try se ejecuta cuando no ha habido ninguna
excepción durante la ejecución del try: .

2. El bloque finally: de la sentencia try es siempre executado.


3. La sintaxis except Exception_Name as exception_object: te permite interceptar
un objeto que contiene información sobre una excepción pendiente. La propiedad del
objeto llamada args (una tupla) almacena todos los argumentos pasados al constructor
del objeto.

4. Las clases de excepciones pueden extenderse para enriquecerlas con nuevas


capacidades o para adoptar sus características a excepciones recién definidas.

Por ejemplo:

try:
assert __name__ == "__main__"
except:
print("fallido", end=' ')
else:
print("éxito", end=' ')
finally:
print("terminado")

El código da como salida: éxito terminado .

Ejercicio 1

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

import math

try:
print(math.sqrt(9))
except ValueError:
print("inf")
else:
print("ok")

Revisar
3.0
ok

Ejercicio 2

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

import math

try:
print(math.sqrt(-9))
except ValueError:
print("inf")
else:
print("ok")
finally:
print("fin")

Revisar
inf
fin

Ejercicio 3

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

import math

class NewValueError(ValueError):
def __init__(self, name, color, state):
self.data = (name, color, state)

try:
raise NewValueError("Advertencia enemiga", "Alerta roja", "Alta
disponibilidad")
except NewValueError as nve:
for arg in nve.args:
print(arg, end='! ')

Revisar

Advertencia enemiga! Alerta roja! Alta disponibilidad!

3.6.1.10 Finalización del Módulo

¡Felicidades! Has completado FP2: Módulo 3.


¡Bien hecho! Has llegado al final del Módulo 3 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 se te ha familiarizado en el Módulo 3:

• Los fundamentos y conceptos básicos de la programación orientada a objetos.


• Las diferencias entre los enfoques procedimental y orientado a objetos en el
ejemplo de la pila.
• Propiedades (variables de instancia y clase, atributos).
• Métodos (métodos de clases y objetos, el constructor, parámetros y propiedades).
• El concepto de herencia (funciones, métodos, jerarquías de clases, polimorfismo,
composición, herencia única frente a herencia múltiple).
• La naturaleza orientada a objetos de las excepciones de Python.
Ahora estás listo para realizar el cuestionario del módulo e intentar el desafío final: el
Examen del Módulo 3, que te ayudará a evaluar lo que has aprendido hasta ahora.

Fundamentos de Python 2 - Módulo 4

Fundamentos de Python 2:
Módulo 4
Misceláneo

En este módulo, aprenderás sobre:

• Generadores, iteradores y cierres.


• Cómo trabajar con un sistema de archivos, un árbol de directorios y archivos.
• Módulos seleccionados de la biblioteca estándar de Python (os, datetime, time,
y calendar).

4.1.1.1 Generadores y cierres

Generadores, donde 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)

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)

4.1.1.2 Generadores y cierres

Generadores, donde encontrarlos: continuación


El protocolo iterador es una forma en que un objeto debe comportarse para ajustarse a
las reglas impuestas por el contexto de las sentencias for e in . Un objeto conforme al
protocolo iterador se llama iterador.

Un iterador debe proporcionar dos métodos:

• __iter__() el cual debe devolver el objeto en sí y que se invoca una vez (es
necesario para que Python inicie con éxito la iteración).
• __next__() el cual debe devolver el siguiente valor (primero, segundo, etc.) de la
serie deseada: será invocado por las sentencias for / in para pasar a la siguiente
iteración; si no hay más valores a proporcionar, el método deberá generar la
excepción StopIteration .

¿Suena extraño? De ningúna manera. Mira el ejemplo en el editor.

Hemos creado una clase capaz de iterar a través de los primeros n valores (donde n es
un parámetro del constructor) de los números de Fibonacci.

Permítenos recordarte: los números de Fibonacci(Fibi) se definen de la siguiente manera:

Fib1 = 1
Fib2 = 1
Fibi = Fibi-1 + Fibi-2

En otras palabras:

• Los primeros dos números de la serie Fibonacci son 1.


• Cualquier otro número de Fibonacci es la suma de los dos anteriores (por
ejemplo, Fib3 = 2, Fib4 = 3, Fib5 = 5, y así sucesivamente).

Vamos a ver el código:

• Las 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).
• Las líneas 8 a 10: el método __iter__ está obligado a devolver el objeto iterador
en sí mismo; su propósito puede ser un poco ambiguo aquí, pero no hay misterio;
trata de imaginar un objeto que no sea un iterador (por ejemplo, es una colección
de algunas entidades), pero uno de sus componentes es un iterador capaz de
escanear la colección; el método __iter__ debe extraer el iterador y confiarle la
ejecución del protocolo de iteración; como puedes ver, el método comienza su
acción imprimiendo un mensaje.
• Las 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 24 y 25 hacen uso del iterador.

El código produce el siguiente resultado:

__init__
__iter__
__next__
1
__next__
1
__next__
2
__next__
3
__next__
5
__next__
8
__next__
13
__next__
21
__next__
34
__next__
55
__next__
salida

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

4.1.1.3 Generadores y cierres

Generadores, donde encontrarlos: continuación


El ejemplo muestra una solución donde el objeto iterador es parte de una clase más
compleja.

El código no es sofisticado, pero presenta el concepto de una manera clara.

Echa un vistazo al código en el editor.

Hemos colocado 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
if self.__i in [1, 2]:
return 1
ret = self.__p1 + self.__p2
self.__p1, self.__p2 = self.__p2, ret
return ret

class Class:
def __init__(self, n):
self.__iter = Fib(n)

def __iter__(self):
print("Class iter")
return self.__iter

object = Class(8)

for i in object:
print(i)

4.1.1.4 Generadores y cierres

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 subsecuentes de __iter__ .
Por ejemplo, el iterador Fib se ve obligado a almacenar con precisión el lugar en el que
se detuvo la última invocación (es decir, el número evaluado y los valores de los dos
elementos anteriores). Esto hace que el código sea más grande y menos comprensible.

Es por eso que Python ofrece una forma mucho más efectiva, conveniente y elegante de
escribir iteradores.

El concepto se basa fundamentalmente en un mecanismo muy específico proporcionado


por la palabra clave reservada yield .

Se puede ver a la palabra clave reservada yield como un hermano más inteligente de la
sentencia return , con una diferencia esencial.

Echa un vistazo a esta función:

def fun(n):
for i in range(n):
return i

Se ve extraño, ¿no? Está claro que el bucle for no tiene posibilidad de terminar su
primera ejecución, ya que el return lo romperá irrevocablemente.

Además, invocar la función no cambiará nada: el bucle for comenzará desde cero y se
romperá inmediatamente.

Podemos decir que dicha función no puede guardar y restaurar su estado en


invocaciones posteriores.

Esto también significa que una función como esta no se puede usar como generador.

Hemos reemplazado exactamente una palabra en el código, ¿puedes verla?

def fun(n):
for i in range(n):
yield i

Hemos puesto yield en lugar de return . Esta pequeña enmienda convierte la función
en un generador, y el ejecutar la sentencia yield tiene algunos efectos muy interesantes.

Primeramente, proporciona el valor de la expresión especificada después de la palabra


clave reservada yield , al igual que return , pero no pierde el estado de la función.

Todos los valores de las variables están congelados y esperan la próxima invocación,
cuando se reanuda la ejecución (no desde cero, como ocurre después de un return ).

Hay una limitación importante: dicha función no debe invocarse explícitamente ya que no
es una función; es un objeto generador.
La invocación devolverá el identificador del objeto, no la serie que esperamos del
generador.

Debido a las mismas razones, la función anterior (la que tiene el return ) solo se puede
invocar explícitamente y no se debe usar como generador.

Cómo construir un generador:


Permítenos mostrarte el nuevo generador en acción.

Así es como podemos usarlo:

def fun(n):
for i in range(n):
yield i

for v in fun(5):
print(v)

¿Puedes adivinar la salida?

Revisar
0
1
2
3
4

4.1.1.5 Generadores y cierres

Cómo construir tu propio generador


¿Qué pasa si necesitas un generador para producir las primeras n potencias de 2 ?

Nada difícil. Solo mira el código en el editor.

def powers_of_2(n):
power = 1
for i in range(n):
yield power
power *= 2

for v in powers_of_2(8):
print(v)
¿Puedes adivinar la salida? Ejecuta el código para verificar tus conjeturas.

Listas por comprensión

Los generadores también se pueden usar con listas por comprensión, justo como aquí:

def powers_of_2(n):
power = 1
for i in range(n):
yield power
power *= 2

t = [x for x in powers_of_2(5)]
print(t)

Ejecuta el ejemplo y verifica la salida.

La función list()

La función list() puede transformar una serie de invocaciones de generador


subsequentes en una lista real:

def powers_of_2(n):
power = 1
for i in range(n):
yield power
power *= 2

t = list(powers_of_2(3))
print(t)

Nuevamente, intenta predecir el resultado y ejecuta el código para verificar tus


predicciones.

El operador in

Además, el contexto creado por el operador in también te permite usar un generador.

El ejemplo muestra cómo hacerlo:

def powers_of_2(n):
power = 1
for i in range(n):
yield power
power *= 2
for i in range(20):
if i in powers_of_2(4):
print(i)

¿Cuál es la salida del código? Ejecuta el programa y verifica.

El generador de números Fibonacci

Ahora veamos un generador de números de la serie Fibonacci, asegurandonos que se


vea mucho mejor que la versión orientada a objetos basada en la implementación directa
del protocolo iterador.

Aquí está:

def fibonacci(n):
p = pp = 1
for i in range(n):
if i in [0, 1]:
yield 1
else:
n = p + pp
pp, p = p, n
yield n

fibs = list(fibonacci(10))
print(fibs)

Adivina la salida (una lista) producida por el generador y ejecuta el código para verificar si
tenías razón.

4.1.1.6 Generadores y cierres

Más acerca de listas por comprensión


Debes poder recordar las reglas que rigen la creación y el uso de un fenómeno de Python
llamado listas por comprensión: 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 listas por comprensión y construye la lista en el momento, sin necesidad de un
bucle o cualquier otro código.
Pareciera que la lista se crea dentro de sí misma; esto es falso, ya que Python tiene que
realizar casi las mismas operaciones que en la primera parte, pero el segundo
formalismo es simplemente más elegante y le evita al lector cualquier detalle innecesario.

El ejemplo genera dos líneas idénticas que contienen el siguiente texto:

[1, 10, 100, 1000, 10000, 100000]


[1, 10, 100, 1000, 10000, 100000]
salida

Ejecuta el código para verificar si tenemos razón.

list_1 = []

for ex in range(6):
list_1.append(10 ** ex)

list_2 = [10 ** ex for ex in range(6)]

print(list_1)
print(list_2)

4.1.1.7 Generadores y cierres

Más acerca de listas por comprensión: continuación


Hay una sintaxis muy interesante que queremos mostrarte ahora. Su usabilidad no se
limita a listas por comprensión.

Es una expresión condicional: una forma de seleccionar uno de dos valores diferentes en
función del resultado de una expresión Booleana.

Observa:

expresión_uno if condición else expresión_dos

Puede parecer un poco sorprendente a primera vista, pero hay que tener en cuenta que
no es una instrucción condicional. Además, no es una instrucción en lo absoluto. Es un
operador.

El valor que proporciona es expresión_uno cuando la condición es True (verdadero),


y expresión_dos cuando sea falso.

Un buen ejemplo te dirá más. Mira el código en el editor.

El código llena una lista con unos y ceros , si el índice de un elemento particular es impar,
el elemento se establece a 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 comprensión de lista? Sí, por supuesto.

the_list = []

for x in range(10):
the_list.append(1 if x % 2 == 0 else 0)

print(the_list)

4.1.1.8 Generadores y cierres

Más acerca de listas por comprensión: continuación


Observa el ejemplo en el editor.

Compacto y elegante: estas dos palabras vienen a la mente al mirar el código.

Entonces, ¿qué tienen en común, generadores y listas por comprensión? ¿Hay alguna
conexión entre ellos? Si. Una conexión algo suelta, pero inequívoca.

Solo un cambio puede convertir cualquier comprensión en un generador.

Listas por comprensión frente a generadores

Ahora observa el código a continuación y ve si puedes encontrar el detalle que convierte


una comprensión de lista en un generador:

the_list = [1 if x % 2 == 0 else 0 for x in range(10)]


the_generator = (1 if x % 2 == 0 else 0 for x in range(10))

for v in the_list:
print(v, end=" ")
print()

for v in the_generator:
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
salida
¿Cómo puedes saber que la segunda asignación crea un generador, no una lista?

Hay algunas pruebas que podemos mostrarte. Aplica la función len() a ambas
entidades.

len(the_list) dará como resultado 10 . Claro y


predecible. len(the_generator) generará una excepción, y verás el siguiente mensaje:

TypeError: object of type 'generator' has no len()


salida

Por supuesto, guardar la lista o el generador no es necesario; puedes crearlos


exactamente en el lugar donde los necesites, justo 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 subsecuentes producidos por
el generador, uno por uno.

Realiza tus propios experimentos.

the_list = [1 if x % 2 == 0 else 0 for x in range(10)]

print(the_list)

4.1.1.9 Generadores y cierres

La función lambda
La función lambda es un concepto tomado de las matemáticas, más específicamente, de
una parte llamada el Cálculo Lambda, pero estos dos fenómenos no son iguales.

Los matemáticos usan el Cálculo Lambda en sistemas formales conectados con: la lógica,
la recursividad o la demostrabilidad de teoremas. Los programadores usan la
función lambda para simplificar el código, hacerlo más claro y fácil de entender.

Una función lambda es una función sin nombre (también puedes llamarla una función
anónima). Por supuesto, tal afirmación plantea inmediatamente la pregunta: ¿cómo se
usa algo que no se puede identificar?
Afortunadamente, no es un problema, ya que se puede mandar llamar dicha función si
realmente se necesita, pero, en muchos casos la función lambda puede existir y
funcionar mientras permanece completamente de incógnito.

La declaración de la función lambda no se parece a una declaración de función normal;


compruébalo tu mismo:

lambda parámetros: expresión

Tal cláusula devuelve el valor de la expresión al tomar en cuenta el valor del


argumento lambda actual.

Como de costumbre, un ejemplo será útil. Nuestro ejemplo usa tres funciones lambda ,
pero con nombres. Analízalo cuidadosamente:

two = lambda: 2
sqr = lambda x: x * x
pwr = lambda x, y: x ** y

for a in range(-2, 3):


print(sqr(a), end=" ")
print(pwr(a, two()))

Vamos a analizarlo:

• La primer lambda es una función anónima sin parámetros 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 parámetros y devuelve el valor del primero elevado al
segundo. El nombre de la variable que lleva la lambda habla por si mismo. No se
utiliza pow para evitar confusiones con la función incorporada del mismo nombre
y el mismo propósito.

El programa produce el siguiente resultado:

4 4
1 1
0 0
1 1
4 4
salida
Este ejemplo es lo suficientemente claro como para mostrar como se declaran las
funciones lambda y cómo se comportan, pero no dice nada acerca de por que son
necesarias y para qué se usan, ya que se pueden reemplazar con funciones de Python de
rutina.

¿Dónde está el beneficio?

4.1.1.10 Generadores y cierres

¿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 print_function ) que imprime
los valores de una (otra) función dada para un conjunto de argumentos seleccionados.

Queremos que print_function 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 print_function() 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 poly() , esta es la función cuyos
valores vamos a imprimir. El cálculo que realiza la función no es muy sofisticado: es el
polinomio (de ahí su nombre) de la forma:

f(x) = 2x2 - 4x + 2

El nombre de la función se pasa a print_function() 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
salida
¿Podemos evitar definir la función poly() , ya que no la vamos a usar más de una vez? Si,
podemos: este es el beneficio que puede aportar una función lambda.

Mira el ejemplo de abajo. ¿Puedes ver la diferencia?

def print_function(args, fun):


for x in args:
print('f(', x,')=', fun(x), sep='')

print_function([x for x in range(-2, 3)], lambda x: 2 * x**2 - 4 * x + 2)

La función print_function() se ha mantenido exactamente igual, pero no hay una


función poly() . Ya no la necesitamos, ya que el polinomio ahora está directamente
dentro de la invocación de la función print_function() en forma de una lambda
definida de la siguiente manera:

lambda x: 2 * x**2 - 4 * x + 2

El código se ha vuelto más corto, más claro y más legible.

Permítenos mostrarte otro lugar donde las lambdas pueden ser útiles. Comenzaremos
con una descripción de map() , una función integrada de Python. Su nombre no es
demasiado descriptivo, su idea es simple y la función en sí es muy utilizable.

def print_function(args, fun):


for x in args:
print('f(', x,')=', fun(x), sep='')

def poly(x):
return 2 * x**2 - 4 * x + 2

print_function([x for x in range(-2, 3)], poly)

4.1.1.11 Generadores y cierres

Lambdas y la función map()


En el más simple de todos los casos posibles, la función map() :

map(function, list)

Toma dos argumentos:

• Una función.
• Una lista.

La descripción anterior está extremadamente simplificada, ya que:


• El segundo argumento map() puede ser cualquier entidad que se pueda iterar
(por ejemplo, una tupla o un generador).
• map() puede aceptar más de dos argumentos.

La función map() aplica la función pasada por su primer argumento a todos los
elementos de su segundo argumento y devuelve un iterador que entrega todos los
resultados de funciones subsequentes.

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 list_1 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 list_1 .
• list_2 se imprime.
• 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 la segunda lambda aquí - solo eleva al cuadrado
cada elemento de list_2 .

Intenta imaginar el mismo código sin lambdas. ¿Sería mejor? Es improbable.

list_1 = [x for x in range(5)]


list_2 = list(map(lambda x: 2 ** x, list_1))
print(list_2)

for x in map(lambda x: x * x, list_2):


print(x, end=' ')
print()

4.1.1.12 Generadores y cierres

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 devuelven 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]
salida

from random import seed, randint

seed()
data = [randint(-10,10) for x in range(5)]
filtered = list(filter(lambda x: x > 0 and x % 2 == 0, data))

print(data)
print(filtered)

4.1.1.13 Generadores y cierres

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 outer(par):
loc = par

var = 1
outer(var)

print(var)
print(loc)
El ejemplo es obviamente erróneo.

Las dos últimas líneas provocarán una excepción NameError - ni par ni loc son
accesibles fuera de la función. Ambas variables existen cuando y solo cuando la
función exterior() esta siendo ejecutada.

Observa el ejemplo en el editor. Hemos modificado el código significativamente.

Hay un elemento completamente nuevo - una función (llamada inner ) dentro de otra
función (llamada outer ).

¿Cómo funciona? Como cualquier otra función excepto por el hecho de que inner() solo
se puede invocar desde dentro de outer() . Podemos decir que inner() es una
herramienta privada de outer() , ninguna otra parte del código la puede acceder.

Observa cuidadosamente:

• La función inner() devuelve el valor de la variable accesible dentro de su


alcance, ya que interior() puede utilizar cualquiera de las entidades a
disposición de outer() .
• La función outer() devuelve la función inner() por si misma; mejor dicho,
devuelve una copia de la función inner() al momento de la invocación de la
función outer() ; 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 outer() ya ha dejado de existir.

En efecto, el código es totalmente válido y genera:

1
salida

La función devuelta durante la invocación de outer() es un cierre.

def outer(par):
loc = par

def inner():
return loc
return inner

var = 1
fun = outer(var)
print(fun())

4.1.1.14 Generadores y cierres


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 outer(par):
loc = par

def inner():
return loc
return inner

var = 1
fun = outer(var)
print(fun())

La función inner() 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 power() .

Esto significa que el cierre no solo utiliza el ambiente congelado, sino que también
puede modificar su comportamiento utilizando valores tomados del exterior.

Este ejemplo muestra una circunstancia más interesante: puedes crear tantos cierres
como quieras usando el mismo código. Esto se hace con una función
llamada make_closure() . Nota:

• El primer cierre obtenido de make_closure() 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
salida

Realiza tus propias pruebas.

def make_closure(par):
loc = par

def power(p):
return p ** loc
return power

fsqr = make_closure(2)
fcub = make_closure(3)

for i in range(5):
print(i, fsqr(i), fcub(i))

4.1.1.15 RESUMEN DE SECCIÓN

Puntos Clave
1. Un iterator es un objeto de una clase que proporciona al menos dos métodos (sin
contar el constructor):

• __iter__() se invoca una vez cuando se crea el iterador y devuelve


el propio objeto del iterador.
• __next__() se invoca para proporcionar el valor de la siguiente iteración y
genera la excepción StopIteration cuando la iteración llega a su fin.

2. La sentencia yield solo puede ser utilizada dentro de funciones. La


sentencia yield suspende la ejecución de la función y hace que la función regrese el
argumento de yield como resultado. Esta función no puede invocarse de forma regular,
su único propósito es ser utilizada como un generador (es decir, en un contexto que
requiera una serie de valores, como un bucle for ).

3. Una expresión condicional es una expresión construida usando el operador if-else .


Por ejemplo:

print(True if 0 >= 0 else False)

Da como salida: True .

4. Una lista por comprensión se convierte en un generador cuando se emplea dentro


de paréntesis (usado entre corchetes, produce una lista regular). Por ejemplo:

for x in (el * 2 for el in range(5)):


print(x)

Da como salida: 02468 .

4. Una función lambda es una herramienta para crear funciones anónimas. Por ejemplo:

def foo(x, f):


return f(x)
print(foo(9, lambda x: x ** 0.5))

Da como salida: 3.0 .

5. La función map(fun, list) crea una copia del argumento list , y aplica la
función fun a todos sus elementos, retornando un generador que proporciona el nuevo
contenido de la lista elemento por elemento. Por ejemplo:

short_list = ['mython', 'python', 'fell', 'on', 'the', 'floor']


new_list = list(map(lambda s: s.title(), short_list))
print(new_list)

Da como salida: ['Mython', 'Python', 'Fell', 'On', 'The', 'Floor'] .

6. La función filter(fun, list) crea una copia de aquellos elementos de list , lo cual
hace que la función fun retorne True . El resultado de la función es
un generador proporcionando el nuevo contenido de la lista elemento por elemento. Por
ejemplo

short_list = [1, "Python", -1, "Monty"]


new_list = list(filter(lambda s: isinstance(s, str), short_list))
print(new_list)

Da como salida: ['Python', 'Monty'] .

7. Un cierre es una técnica que permite almacenar valores a pesar de que el contexto en
el que han sido creados no existe más. Por ejemplo:

def tag(tg):
tg2 = tg
tg2 = tg[0] + '/' + tg[1:]

def inner(str):
return tg + str + tg2
return inner

b_tag = tag('<b>')
print(b_tag('Monty Python'))

Da como salida: <b>Monty Python</b>

Ejercicio 1

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

class Vowels:
def __init__(self):
self.vow = "aeiouy " # Sí, sabemos que y no siempre se
considera una vocal.
self.pos = 0

def __iter__(self):
return self

def __next__(self):
if self.pos == len(self.vow):
raise StopIteration
self.pos += 1
return self.vow[self.pos - 1]

vowels = Vowels()
for v in vowels:
print(v, end=' ')

Revisar
a e i o u y

Ejercicio 2

Escribe una función lambda, estableciendo a 1 su argumento entero, y aplícalo a la


función map() para producir la cadena 1 3 3 5 en la consola.

any_list = [1, 2, 3, 4]
even_list = # Completar las líneas aquí.
print(even_list)

Revisar
list(map(lambda n: n | 1, any_list))

Ejercicio 3

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

def replace_spaces(replacement='*'):
def new_replacement(text):
return text.replace(' ', replacement)
return new_replacement

stars = replace_spaces()
print(stars("And Now for Something Completely Different"))

Revisar

And*Now*for*Something*Completely*Different

Nota

PEP 8, la Guía de Estilo para Código Python, recomienda que las funciones lambdas no
deben asignarse a variables, sino que deben definirse como funciones.
Esto significa que es mejor utilizar una sentencia def , y evita usar una sentencia de
asignación que vincule una expresión lambda a un identificador. Analiza el código a
continuación:

# Recomendado:

def f(x): return 3*x

# No recomendado:
f = lambda x: 3*x

La vinculación de lambdas a identificadores generalmente duplica la funcionalidad de la


declaración def . El uso de sentencias def , por otro lado, genera más líneas de código.

Es importante comprender que a la realidad a menudo le gusta dibujar sus propios


escenarios, que no necesariamente siguen las convenciones o recomendaciones
formales. Si decides seguirlos o no, dependerá de muchas cosas: tus preferencias, otras
convenciones adoptadas, las pautas internas de la empresa, la compatibilidad con el
código existente, etc. Toma en cuenta esto.

4.2.1.1 Procesando archivos

Accediendo archivos desde 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 que hemos esperado hasta ahora para mostrarte esto.

La respuesta es muy simple: la forma en que Python accede y procesa los archivos se
implementa utilizando un conjunto consistente de objetos. No hay mejor momento para
hablar de esto.

4.2.1.2 Procesando archivos


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 (por ejemplo, C: ) y todos los directorios crecen desde un directorio raíz llamado / ,
mientras que los sistemas Windows reconocen el directorio raíz como \ .

Además, los nombres de archivo de sistemas Unix/Linux distinguen entre mayúsculas y


minúsculas. Los sistemas Windows almacenan mayúsculas y minúsculas en el nombre del
archivo, pero no distinguen entre ellas.

Esto significa que estas dos


cadenas: EsteEsElNombreDelArchivo y esteeselnombredelarchivo describen dos
archivos diferentes en sistemas Unix/Linux, pero tienen el mismo nombre para un solo
archivo en sistemas Windows.
La diferencia principal y más llamativa es que debes usar dos separadores diferentes
para los nombres de directorio: \ en Windows y / en Unix/Linux.

Esta diferencia no es muy importante para el usuario normal, pero es muy importante al
escribir programas en Python.

Para entender por qué, intenta recordar el papel muy específico que
desempeña \ dentro de las cadenas en Python.

4.2.1.3 Procesando archivos


Nombres de archivos: continuación
Supongamos que estás interesado en un archivo en particular ubicado en el
directorio dir, y con el nombre de file.

Supongamos también que deseas asignar a una cadena el nombre del archivo.

En sistemas Unix/Linux, sería de la siguiente manera:

name = "/dir/file"

Pero si intentas codificarlo para el sistema Windows:

name = "\dir\file"

Obtendrás una sorpresa desagradable: Python generará un error o la ejecución del


programa se comportará de manera extraña, como si el nombre del archivo se hubiera
distorsionado de alguna manera.

De hecho, no es extraño en lo absoluto, pero es bastante obvio y natural. Python usa


la \ como un carácter de escape (como \n ).

Esto significa que los nombres de archivo de Windows deben escribirse de la siguiente
manera:

name = "\\dir\\file"

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:

name = "/dir/file"
name = "c:/dir/file"

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 o manejadores (un tipo de puntero inteligente) o streams (una especie de
canal) (los usaremos como sinónimos aquí).

El programador, que tiene un conjunto de funciones y métodos, puede realizar ciertas


operaciones en el stream, que afectan los archivos reales utilizando mecanismos
contenidos en el núcleo del sistema operativo.

De esta forma, puedes implementar el proceso de acceso a cualquier archivo, incluso


cuando el nombre del archivo es desconocido al momento de escribir el programa.

Las operaciones realizadas con el stream abstracto reflejan las actividades relacionadas
con el archivo físico.

Para conectar (vincular) el stream con el archivo, es necesario realizar una operación
explícita.

La operación de conectar un stream con un archivo es llamada abrir el archivo, mientras


que desconectar el enlace se denomina cerrar el archivo.
Por lo tanto, la conclusión es que la primera operación realizada en el stream es
siempre open (abrir) y la ultima es close (cerrar) . El programa, en efecto, es libre
de manipular el stream entre estos dos eventos y manejar el archivo asociado.

Esta libertad está limitada por las características físicas del archivo y la forma en que se
abrió el archivo.

Digamos nuevamente que la apertura del stream puede fallar, y puede ocurrir debido a
varias razones: la más común es la falta de un archivo con un nombre específico.

También puede suceder que el archivo físico exista, pero el programa no puede abrirlo.
También existe el riesgo de que el programa haya abierto demasiados streams, y el
sistema operativo específico puede no permitir la apertura simultánea de más
de n archivos (por ejemplo, 200).

Un programa bien escrito debe detectar estas aperturas fallidas y reaccionar en


consecuencia.

4.2.1.4 Procesando archivos


Archivos: streams
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 open mode
(modo de apertura).

Si la apertura es exitosa, el programa solo podrá realizar las operaciones que sean
consistentes con el modo abierto declarado.

Hay dos operaciones básicas a realizar con el stream:

• Lectura del stream: las porciones de los datos se recuperan del archivo y se
colocan en un área de memoria administrada por el programa (por ejemplo, una
variable).
• Escritura del stream: Las porciones de los datos de la memoria (por ejemplo, una
variable) se transfieren al archivo.

Hay tres modos básicos utilizados para abrir un stream:

• Modo Lectura: un stream abierto en este modo permite solo operaciones de


lectura; intentar escribir en la transmisión provocará una excepción (la excepción
se llama UnsupportedOperation, la cual hereda el OSError y el ValueError, y
proviene del módulo io).
• Modo Escritura: un stream abierto en este modo permite solo operaciones de
escritura; intentar leer el stream provocará la excepción mencionada
anteriormente.
• Modo Actualizar: un stream abierto en este modo permite tanto lectura como
escritura.
Antes de discutir como 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.

4.2.1.5 Procesando archivos


Manejo de archivos
Python supone que cada archivo está oculto detrás de un objeto de una clase adecuada.

Por supuesto, es difícil no preguntar cómo interpretar la palabra adecuada.

Los archivos se pueden procesar de muchas maneras diferentes: algunos dependen del
contenido del archivo, otros de las intenciones del programador.

En cualquier caso, diferentes archivos pueden requerir diferentes conjuntos de


operaciones y comportarse de diferentes maneras.

Un objeto de una clase adecuada es creado cuando abres el archivo y lo aniquilas al


momento de cerrarlo.

Entre estos dos eventos, puedes usar el objeto para especificar que 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 que pronto.

4.2.1.6 Procesando archivos


Manejo de archivos: continuación
Debido al tipo de contenido de los streams, todos se dividen en tipo texto y binario.

Los streams de texto están estructurados en líneas; es decir, contienen caracteres


tipográficos (letras, dígitos, signos de puntuación, etc.) dispuestos en filas (líneas), como
se ve a simple vista cuando se mira el contenido del archivo en el editor.

Este tipo de archivo es escrito (o leído) principalmente carácter por carácter, o línea por
línea.

Los streams binarios no contienen texto, sino una secuencia de bytes de cualquier valor.
Esta secuencia puede ser, por ejemplo, un programa ejecutable, una imagen, un audio o
un videoclip, un archivo de base de datos, etc.

Debido a que estos archivos no contienen líneas, las lecturas y escrituras se relacionan
con porciones de datos de cualquier tamaño. Por lo tanto, los datos se leen y escriben
byte a byte, o bloque a bloque, donde el tamaño del bloque generalmente varía de uno a
un valor elegido arbitrariamente.

Ahora viene un problema pequeño. En los sistemas Unix/Linux, los extremos de la línea
están marcados por un solo carácter llamado LF (código ASCII 10) designado en los
programas de Python como \n .
Otros sistemas operativos, especialmente los derivados del sistema prehistórico CP/M
(que también aplica a los sistemas de la familia Windows) utilizan una convención
diferente: el final de la línea está marcada por un par de caracteres, CR y LF (códigos
ASCII 13 y 10) los cuales se puede codificar como \r\n .

Esta ambigüedad puede causar varias consecuencias desagradables.

Si creas un programa responsable de procesar un archivo de texto y está escrito para


Windows, puedes reconocer los extremos de las líneas al encontrar los caracteres \r\n ,
pero si el mismo programa se ejecuta en un entorno Unix/Linux será completamente
inútil, y viceversa: el programa escrito para sistemas Unix/Linux podría ser inútil en
Windows.

Estas características indeseables del programa, que impiden o dificultan el uso del
programa en diferentes entornos, se denomina falta de portabilidad.

Del mismo modo, el rasgo del programa que permite la ejecución en diferentes entornos
se llama portabilidad. Un programa dotado de tal rasgo se llama programa portable.

4.2.1.7 Procesando archivos


Manejo de archivos: continuación
Dado que los problemas de portabilidad eran (y siguen siendo) muy graves, se tomó la
decisión de resolver definitivamente el problema de una manera que no atraiga mucho la
atención del desarrollador.

Se realizó a nivel de clases, que son responsables de leer y escribir caracteres hacia y
desde el stream. Funciona de la siguiente manera:
• Cuando el stream está abierto y se recomienda que los datos en el archivo
asociado se procesen como texto (o no existe tal aviso), se cambia al modo texto.
• Durante la lectura y escritura de líneas desde y hacia el archivo asociado, no
ocurre nada especial en el entorno Unix, pero cuando se realizan las mismas
operaciones en el entorno Windows, un proceso llamado traducción de caracteres
de nueva línea ocurre: cuando lees una línea del archivo, cada par de
caracteres \r\n se reemplaza con un solo carácter \n , y viceversa; durante las
operaciones de escritura, cada carácter \n se reemplaza con un par de
caracteres \r\n .
• El mecanismo es completamente transparente para el programa, el cual puede
escribirse como si estuviera destinado a procesar archivos de texto Unix/Linux
solamente; el código fuente ejecutado en un entorno Windows también
funcionará correctamente.
• Cuando el stream está abierto, su contenido se toma tal cual es, sin ninguna
conversión - no se agregan, ni se omiten bytes.

Vamos a analizarlo:

• El nombre de la función ( open ) habla por si mismo; si la apertura es exitosa, la


función devuelve un objeto stream; de lo contrario, se genera una excepción (por
ejemplo, FileNotFoundError si el archivo que vas a leer no existe).

• El primer parámetro de la función ( file ) especifica el nombre del archivo que se


asociará al stream.

• El segundo parámetro ( mode ) especifica el modo de apertura utilizado para el


stream; es una cadena llena de una secuencia de caracteres, y cada uno de ellos
tiene su propio significado especial (más detalles pronto).

• El tercer parámetro ( encoding ) especifica el tipo de codificación (por ejemplo,


UTF-8 cuando se trabaja con archivos de texto).

• La apertura debe ser la primera operación realizada en el stream.

Nota: el modo y los argumentos de codificación pueden omitirse; en dado caso, se


tomarán sus valores predeterminados. El modo de apertura predeterminado es leer en
modo de texto, mientras que la codificación predeterminada depende de la plataforma
utilizada.

Permítenos ahora presentarte los modos de apertura más importantes y útiles. ¿Listo?

4.2.1.8 Procesando archivos


Modos para abrir los streams
r modo de apertura: 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() generará una excepción.

w modo de apertura: 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 borra); si la creación no es posible (por
ejemplo, debido a permisos del sistema) la función open() generará una
excepción.

a modo de apertura: 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).

r+ modo de apertura: lectura y actualización

• El stream será abierto en modo lectura y actualización.


• El archivo asociado con el stream debe existir y tiene que permitir escritura, de lo
contrario la función open() generará una excepción.
• Se permiten operaciones de lectura y escritura en el stream.

w+ modo de apertura: escritura y actualización

• El stream será abierto en modo escritura y actualización.


• El archivo asociado con el stream no necesita existir; si no existe, se creará; el
contenido anterior del archivo permanece intacto.
• Se permiten operaciones de lectura y escritura en el stream.

Seleccionando modo de texto y modo binario


Si hay una letra b al final de la cadena del modo significa que el stream se debe abrir en
el modo binario.

Si la cadena del modo termina con una letra t el stream es abierto en modo texto.

El modo texto es el comportamiento predeterminado que se utiliza cuando no se


especifica ya sea modo binario o texto.

Finalmente, la apertura exitosa del archivo establecerá la posición actual del archivo (el
cabezal virtual de lectura/escritura) antes del primer byte del archivo si el modo no es a y
después del último byte del archivo si el modo es a .
Modo texto Modo binario Descripción

rt rb lectura

wt wb escritura

at ab adjuntar

r+t r+b lectura y actualización

w+t w+b escritura y actualización

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() generará una excepción.

4.2.1.9 Procesando archivos


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")
# El procesamiento va aquí.
stream.close()
except Exception as exc:
print("No se puede abrir el archivo:", exc)

¿Que está pasando aqui?

• Hemos abierto el bloque try-except ya que queremos manejar los errores de


tiempo de ejecución suavemente.
• Se emplea la función open() para intentar abrir el archivo especificado (ten en
cuenta la forma en que hemos especificado el nombre del archivo).
• El modo de apertura se define como texto para leer (como texto es la
configuración predeterminada, podemos omitir la t en la cadena de modo).
• En caso de éxito obtenemos un objeto de la función open() y lo asignamos a la
variable del stream.
• Si open() falla, manejamos la excepción imprimiendo la información completa del
error (es bueno saber qué sucedió exactamente).
Streams pre-abiertos
Dijimos anteriormente que cualquier operación del stream debe estar precedida por la
invocación de la función open() . Hay tres excepciones bien definidas a esta regla.

Cuando comienza nuestro programa, los tres streams ya están abiertos y no requieren
ninguna preparación adicional. Además, tu programa puede usar estos streams
explícitamente si tienes cuidado de importar el módulo sys :

import sys

Porque ahí es donde se coloca la declaración de estos streams.

Los nombres de los streams son: sys.stdin , sys.stdout y sys.stderr .

Vamos a analizarlos:

• sys.stdin
o stdin (significa entrada estándar).
o El stream stdin normalmente se asocia con el teclado, se abre previamente
para la lectura y se considera como la fuente de datos principal para los
programas en ejecución.
o La función bien conocida input() lee datos de stdin por default.

• sys.stdout
o stdout (significa salida estándar).
o El stream stdout normalmente está asociado con la pantalla, preabierta
para escritura, considerada como el objetivo principal para la salida de
datos por el programa en ejecución.
o La función bien conocida print() envía los datos al stream stdout .

• sys.stderr
o stderr (significa salida de error estándar).
o El stream stderr normalmente está asociado con la pantalla, preabierta
para escribir, considerada como el lugar principal donde el programa en
ejecución debe enviar información sobre los errores encontrados durante
su trabajo.
o No hemos presentado ningún método para enviar datos a este stream (lo
haremos pronto, lo prometemos).
o La separación de stdout (resultados útiles producidos por el programa)
de stderr (mensajes de error, indudablemente útiles pero no proporcionan
resultados) ofrece la posibilidad de redirigir estos dos tipos de información
a los diferentes objetivos. Una discusión más extensa sobre este tema está
más allá del alcance de nuestro curso. El manual del sistema operativo
proporcionará más información sobre estos temas.
4.2.1.10 Procesando archivos
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 genera 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
bufers a descargarse, es posible que dichas descargas fallen y, por lo
tanto, close() falle también.

Ya hemos mencionado fallas causadas por funciones que operan con los streams, pero
no mencionamos nada sobre cómo podemos identificar exactamente la causa de la falla.

La posibilidad de hacer un diagnóstico existe y es proporcionada por uno de los


componentes de excepción de los streams. Hablaremos acerca de ellos a continuación.

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:
# Algunas operaciones con streams.
except IOError as exc:
print(exc.errno)

El valor del atributo errno se puede comparar con una de las constantes simbólicas
predefinidas en módulo errno .

Echemos un vistazo a algunas constantes seleccionadas útiles para detectar errores en


los streams:
• errno.EACCES → Permiso denegado

El error se produce cuando intentas, por ejemplo, abrir un archivo con atributos
de solo lectura para abrirlo.

errno.EBADF → Número de archivo incorrecto

El error se produce cuando intentas, por ejemplo, operar un stream sin abrirlo.

• errno.EEXIST → Archivo existente

El error se produce cuando intentas, por ejemplo, cambiar el nombre de un


archivo con su nombre anterior.

• errno.EFBIG → Archivo demasiado grande

El error ocurre cuando intentas crear un archivo que es más grande que el
máximo permitido por el sistema operativo.

• errno.EISDIR → Es un directorio

El error se produce cuando intentas tratar un nombre de directorio como el


nombre de un archivo ordinario.

• errno.EMFILE → Demasiados archivos abiertos

El error se produce cuando intentas abrir simultáneamente más streams de los


aceptables para el sistema operativo.

• errno.ENOENT → El archivo o directorio no existe

El error se produce cuando intentas acceder a un archivo o directorio inexistente.

• errno.ENOSPC → No queda espacio en el dispositivo

El error ocurre cuando no hay espacio libre en el dispositivo.

La lista completa es mucho más larga (incluye también algunos códigos de error no
relacionados con el procesamiento del los streams).

4.2.1.11 Procesando archivos

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 las que se te presentan en el editor:
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 generará una excepción ValueError.

Ahora podemos simplificar nuestro código de la siguiente manera:

from os import strerror

try:
s = open("c:/users/user/Desktop/file.txt", "rt")
# El procesamiento va aquí.
s.close()
except Exception as exc:
print("El archivo no pudo ser abierto:", 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.

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("Demasiados archivos abiertos.")
else:
print("El numero del error es:", exc.errno)

4.2.1.12 RESUMEN DE SECCIÓN

Puntos Clave
1. Un archivo necesita ser abierto antes de que pueda ser procesado por un programa, y
debe ser cerrado cuando el procesamiento termine.
El abrir un archivo lo asocia con el stream, que es una representación abstracta de los
datos físicos almacenados en los medios. La forma en que se procesa el stream se
llama modo de apertura. Existen tres modos de apertura:

• modo lectura: solo se permiten operaciones de lectura.


• modo escritura: solo se permiten operaciones de escritura.
• modo de actualización: se permiten ambas, lectura y escritura.

2. Dependiendo del contenido del archivo físico, se pueden usar diferentes clases de
Python para procesar archivos. En general, BufferedIOBase es capaz de procesar
cualquier archivo, mientras que TextIOBase es una clase especializada dedicada al
procesamiento de archivos de texto (es decir, archivos que contienen textos visibles para
humanos divididos en líneas usando marcadores de nueva línea). Por lo tanto, los
streams se pueden dividir en binarios y de texto.

3. Las siguientes sintaxis de la funcion open() se utilizan para abrir un archivo:

open(nombre_archivo, modo=modo_apertura,
codificación=codificacion_de_texto)
La invocación crea un objeto stream y lo asocia con el archivo llamado nombre_archivo ,
utilizando el modo modo_apertura y configurando la
especificada codificacion_de_texto , o genera una excepción en caso de un error.

4. Los tres streams predefinidos que ya estan abiertos cuando inicia el programa son:

• sys.stdin – entrada estandar.


• sys.stdout – salida estandar.
• sys.stderr – salida de error estandar.

5. El objeto de la excepción IOError , creado cuando cualquier operación de archivo falla


(incluyendo las operaciones de apertura), contiene una propiedad llamada errno , que
contiene el código de finalización de la acción fallida. Utiliza este valor para diagnosticar el
problema.

Ejercicio 1

¿Cómo se codifica el valor del argumento modo de la función open() si se va a crear un


nuevo archivo de texto?

Revisar

"wt" o "w"
Ejercicio 2

¿Cuál es el significado del valor representado por errno.EACESS ?

Revisar

Permiso denegado: no se permite acceder al contenido del archivo.

Ejercicio 3

¿Cuál es la salida esperada del siguiente código, asumiendo que el archivo llamado file no
existe?

import errno

try:
stream = open("file", "rb")
print("existe")
stream.close()
except IOError as error:
if error.errno == errno.ENOENT:
print("ausente")
else:
print("desconocido")

Revisar

ausente

4.3.1.1 Trabajando con archivos reales

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 y poder procesarlo.

El procesamiento será muy simple: vas a copiar el contenido del archivo a la consola y
contarás todos los caracteres que el programa ha leído.

Pero recuerda: nuestra comprensión de un archivo de texto es muy estricta. Es un


archivo de texto sin formato: puede contener solo texto, sin decoraciones adicionales
(formato, diferentes fuentes, etc.).
Es por eso que debes evitar crear el archivo utilizando un procesador de texto avanzado
como MS Word, LibreOffice Writer o algo así. Utiliza los conceptos básicos que ofrece tu
sistema operativo: Bloc de notas, vim, gedit, etc.

Si tus archivos de texto contienen algunos caracteres nacionales no cubiertos por el juego
de caracteres ASCII estándar, es posible que necesites un paso adicional. La invocación de
tu función open() puede requerir un argumento que denote una codificación específica
del texto.

Por ejemplo, si estás utilizando un sistema operativo Unix/Linux configurado para usar
UTF-8 como una configuración de todo el sistema, la función open() puede verse de la
siguiente manera:

stream = open('file.txt', 'rt', encoding='utf-8')

Donde el argumento de codificación debe establecerse en un valor dentro de una cadena


que representa la codificación de texto adecuada (UTF-8, en este caso).

Consulta la documentación de tu sistema operativo para encontrar el nombre de


codificación adecuado para tu entorno.

Nota

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 IDLE o cualquier otro Entorno de Desarrollo para llevar a cabo tus propias
pruebas.

# Se abre el archivo tzop.txt en modo lectura, devolviéndolo como


# un objeto del tipo archivo:
stream = open("tzop.txt", "rt", encoding = "utf-8")

# Se imprime el contenido del archivo:


print(stream.read())

4.3.1.2 Trabajando con archivos reales

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 cual de ellos prefieres
y te gusta.

Algunos de ellos serán a veces más prácticos y otros más problemáticos. Se flexible. No
tengas miedo de cambiar tus preferencias.
El más básico de estos métodos es el que ofrece la función read() , la cual pudiste ver en
acción en la lección anterior.

Si se aplica a un archivo de texto, la función es capaz de:

• Leer un número determinado de caracteres (incluso solo uno) del archivo y


devolverlos como una cadena.
• Leer todo el contenido del archivo y devolverlo como una cadena.
• Si no hay nada más que leer (el cabezal de lectura virtual llega al final del archivo),
la función devuelve una cadena vacía.

Comenzaremos con la variante más simple y usaremos un archivo llamado text.txt . El


archivo contiene lo siguiente:

Lo hermoso es mejor que lo feo.


Explícito es mejor que implícito.
Simple es mejor que complejo.
Complejo es mejor que complicado.
text.txt

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 carácter del archivo ( char = stream.read(1) ).
• Si tienes éxito (esto se demuestra por el resultado positivo de la condición while ),
se muestra el carácter (nota el argumento end= , ¡es importante! ¡No querrás
saltar a una nueva línea después de cada carácter!).
• Tambien, se actualiza el contador ( counter ),
• Intenta leer el siguiente carácter y el proceso se repite.

from os import strerror

try:
counter = 0
stream = open('text.txt', "rt")
char = stream.read(1)
while char != '':
print(char, end='')
counter += 1
char = stream.read(1)
stream.close()
print("\n\nCaracteres en el archivo:", counter)
except IOError as e:
print("Se produjo un error de E/S:", strerror(e.errno))
4.3.1.3 Trabajando con archivos reales

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. ¿Qué 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() .
• Después, 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:
counter = 0
stream = open('text.txt', "rt")
content = stream.read()
for char in content:
print(char, end='')
counter += 1
stream.close()
print("\n\nCaracteres en el archivo:", counter)
except IOError as e:
print("Se produjo un error de E/S:", strerr(e.errno))

4.3.1.4 Trabajando con archivos reales

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.

Hagamos 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:
character_counter = line_counter = 0
stream = open('text.txt', 'rt')
line = stream.readline()
while line != '':
line_counter += 1
for char in line:
print(char, end='')
character_counter += 1
line = stream.readline()
stream.close()
print("\n\nCaracteres en el archivo:", character_counter)
print("Líneas en el archivo: ", line_counter)
except IOError as e:
print("Se produjo un error de E/S:", strerror(e.errno))

4.3.1.5 Trabajando con archivos reales

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 el siguiente código de ejemplo para entender cómo
funciona el método readlines() :

stream = open("text.txt")
print(stream.readlines(20))
print(stream.readlines(20))
print(stream.readlines(20))
print(stream.readlines(20))
stream.close()

El tamaño máximo del búfer de entrada aceptado se pasa al método como argumento.

Puedes esperar que readlines() procese el contenido del archivo de manera más
efectiva que readline() , ya que puede ser invocado menos veces.

Nota: cuando no hay nada que leer del archivo, el método devuelve una lista vacía. Úsalo
para detectar el final del archivo.

Puedes esperar que al aumentar el tamaño del búfer mejore el rendimiento de entrada,
pero no hay una regla de oro para ello: intenta encontrar los valores óptimos por ti
mismo.

Observa el código en el editor. Lo hemos modificado para mostrarte como


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:
character_counter = line_counter = 0
stream = open('text.txt', 'rt')
lines = stream.readlines(20)
while len(lines) != 0:
for line in lines:
line_counter += 1
for char in line:
print(char, end='')
character_counter += 1
lines = stream.readlines(10)
stream.close()
print("\n\nCaracteres en el archivo:", character_counter)
print("Líneas en el archivo: ", line_counter)
except IOError as e:
print("Se produjo un error de E/S:", strerror(e.errno))
4.3.1.6 Trabajando con archivos reales

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 ninguna 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 cuan simple y claro se ha vuelto el código.

from os import strerror

try:
character_counter = line_counter = 0
for line in open('text.txt', 'rt'):
line_counter += 1
for char in line:
print(char, end='')
character_counter += 1
print("\n\nCaracteres en el archivo: ", character_counter)
print("Líneas en el archivo: ", line_counter)
except IOError as e:
print("Se produjo un error de E/S:", strerror(e.errno))

4.3.1.7 Trabajando con archivos reales

Manejando archivos de texto: write()


Escribir archivos de texto parece ser más simple, ya que hay un método que puede
usarse para realizar dicha tarea.

El método se llama write() y espera solo un argumento: una cadena que se transferirá
a un archivo abierto (no lo olvides), el modo de apertura debe reflejar la forma en que se
transfieren los datos, escribir en un archivo abierto en modo de lectura no tendrá éxito).

No se agrega carácter de nueva línea al argumento de write() , por lo que debes


agregarlo tu mismo si deseas que el archivo se complete con varias líneas.
El ejemplo en el editor muestra un código muy simple que crea un archivo
llamado newtext.txt (nota: el modo de apertura w asegura que el archivo se creará
desde cero, incluso si existe y contiene datos) y luego coloca 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 #2
línea #3
línea #4
línea #5
línea #6
línea #7
línea #8
línea #9
línea #10
salida

¿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:
file = open('newtext.txt', 'wt') # Un nuevo archivo
(newtext.txt) es creado.
for i in range(10):
s = "línea #" + str(i+1) + "\n"
for char in s:
file.write(char)
file.close()
except IOError as e:
print("Se produjo un error de E/S:", strerror(e.errno))

4.3.1.8 Trabajando con archivos reales

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:
file = open('newtext.txt', 'wt')
for i in range(10):
file.write("línea #" + str(i+1) + "\n")
file.close()
except IOError as e:
print("Se produjo un error de E/S:", strerror(e.errno))

4.3.1.9 Trabajando con archivos reales

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

The most important aspect of this is that in the place where we have contact with the
data, we are not able to, or simply don't want to, know anything about it.

Los datos amorfos no pueden almacenarse utilizando ninguno de los medios


presentados anteriormente: no son cadenas ni listas.

Debe haber un contenedor especial capaz de manejar dichos datos.

Python tiene más de un contenedor, uno de ellos es una clase especializada llamada
bytearray, como su nombre indica, es un arreglo que contiene bytes (amorfos).

Si deseas tener dicho contenedor, por ejemplo, para leer una imagen de mapa de bits y
procesarla de alguna manera, debes crearlo explícitamente, utilizando uno de los
constructores disponibles.
Observa:

data = bytearray(10)

Tal invocación crea un objeto bytearray capaz de almacenar diez bytes.

Nota: dicho constructor llena todo el arreglo con ceros.

4.3.1.10 Trabajando con archivos reales

Bytearrays: continuación
Bytearrays se asemejan a listas en muchos aspectos. Por ejemplo, son mutables, son
susceptibles 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.

Ahora te vamos a mostrar como 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))

4.3.1.11 Trabajando con archivos reales

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 10 con algo
como ord('a') , esto producirá bytes que contienen valores correspondientes a
la parte alfabética del código ASCII (no pienses que harás que el archivo sea un
archivo de texto; sigue siendo binario, ya que se creó con un indicador: wb ).
• Después, creamos el archivo usando la función open() , la única diferencia en
comparación con las variantes anteriores es que el modo de apertura contiene el
indicador b .
• El método write() toma su argumento ( bytearray ) y lo envía (como un todo) al
archivo.
• El stream se cierra de forma rutinaria.

El método write() devuelve la cantidad de bytes escritos correctamente.

Si los valores difieren de la longitud de los argumentos del método, puede significar que
hay algunos errores de escritura.

En este caso, no hemos utilizado el resultado; esto puede no ser apropiado en todos los
casos.

Intenta ejecutar el código y analiza el contenido del archivo recién creado.

Lo vas a usar en el siguiente paso.

Cómo leer bytes de un stream


La lectura de un archivo binario requiere el uso de un método especializado
llamado readinto() , ya que el método no crea un nuevo objeto del arreglo de bytes,
sino que llena uno creado previamente con los valores tomados del archivo binario.

Nota:

• El método devuelve el número de bytes leídos con éxito.


• El método intenta llenar todo el espacio disponible dentro de su argumento; si
existen más datos en el archivo que espacio en el argumento, la operación de
lectura se detendrá antes del final del archivo; el resultado del método puede
indicar que el arreglo de bytes solo se ha llenado de manera fragmentaria (el
resultado también lo mostrará y la parte del arreglo que no está siendo utilizada
por los contenidos recién leídos permanece intacta).

Observa el código a continuación:

from os import strerror

data = bytearray(10)

try:
binary_file = open('file.bin', 'rb')
binary_file.readinto(data)
binary_file.close()

for b in data:
print(hex(b), end=' ')
except IOError as e:
print("Se produjo un error de E/S:", strerror(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.

from os import strerror

data = bytearray(10)

for i in range(len(data)):
data[i] = 10 + i

try:
binary_file = open('file.bin', 'wb')
binary_file.write(data)
binary_file.close()
except IOError as e:
print("Se produjo un error de E/S:", strerror(e.errno))

# Ingresa aquí el código que lee los bytes del stream.

4.3.1.12 Trabajando con archivos reales

Cómo leer bytes de un stream


Se ofrece una forma alternativa de leer el contenido de un archivo binario mediante el
método denominado read() .

Invocado sin argumentos, intenta leer todo el contenido del archivo en la memoria,
haciéndolo parte de un objeto recién creado de la clase bytes.

Esta clase tiene algunas similitudes con bytearray , con la excepción de una diferencia
significativa: es immutable.
Afortunadamente, no hay obstáculos para crear un arreglo de bytes tomando su valor
inicial directamente del objeto de bytes, como aquí:

from os import strerror

try:
binary_file = open('file.bin', 'rb')
data = bytearray(binary_file.read())
binary_file.close()

for b in data:
print(hex(b), end=' ')

except IOError as e:
print("Se produjo un error de E/S:", strerror(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:
binary_file = open('file.bin', 'wb')
binary_file.write(data)
binary_file.close()
except IOError as e:
print("Se produjo un error de E/S:", strerror(e.errno))

# Ingresa aquí el código que lee los bytes del stream.

4.3.1.13 Trabajando con archivos reales

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:
binary_file = open('file.bin', 'rb')
data = bytearray(binary_file.read(5))
binary_file.close()
for b in data:
print(hex(b), end=' ')

except IOError as e:
print("Se produjo un error de E/S:", strerror(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:
binary_file = open('file.bin', 'wb')
binary_file.write(data)
binary_file.close()
except IOError as e:
print("Se produjo un error de E/S:", strerror(e.errno))

# Ingresa aquí el código que lee los bytes del stream.

4.3.1.14 Trabajando con archivos reales

Copiando archivos: una herramienta simple y funcional


Ahora vas a juntar todo este nuevo conocimiento, agregarle algunos elementos nuevos y
usarlo para escribir un código real que pueda copiar el contenido de un archivo.

Por supuesto, el propósito no es crear un reemplazo para los comandos como copy de
(MS Windows) o cp de (Unix/Linux) pero para ver una forma posible de crear una
herramienta de trabajo, incluso si nadie quiere usarla.

Observa el código en el editor. Analicémoslo:

• 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 10 a la 16: repiten casi la misma acción, pero esta vez para el archivo de
salida.
• La línea 18: 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 19: cuenta los bytes copiados: este es el contador y su valor inicial.
• Línea 21: intenta llenar el búfer por primera vez.
• Línea 22: mientras se obtenga un número de bytes distinto a 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 prefiere escribir todo el búfer).
• Línea 24: actualiza el contador.
• Línea 25: lee el siguiente fragmento de archivo.
• Las líneas 30 a la 32: limpieza final: el trabajo está hecho.

from os import strerror

source_file_name = input("Ingresa el nombre del archivo fuente: ")


try:
source_file = open(source_file_name, 'rb')
except IOError as e:
print("No se puede abrir archivo fuente: ", strerror(e.errno))
exit(e.errno)

destination_file_name = input("Ingresa el nombre del archivo destino: ")


try:
destination_file = open(destination_file_name, 'wb')
except Exception as e:
print("No se puede crear el archivo de destino:", strerror(e.errno))
source_file.close()
exit(e.errno)

buffer = bytearray(65536)
total = 0
try:
readin = source_file.readinto(buffer)
while readin > 0:
written = destination_file.write(buffer[:readin])
total += written
readin = source_file.readinto(buffer)
except IOError as e:
print("No se puede crear el archivo de destino: ", strerror(e.errno))
exit(e.errno)

print(total,'byte(s) escritos con éxito')


source_file.close()
destination_file.close()

4.3.1.15 LABORATORIO: Histograma de frecuencia de caracteres

LABORATORIO
Tiempo Estimado
60 minutos

Nivel de dificultad
Medio

Objetivos
• Mejorar las habilidades del estudiante al operar con la lectura archivos.
• Utilizar colecciones de datos para contar datos numerosos.

Escenario
Un archivo de texto contiene algo de texto (nada inusual) pero necesitamos saber con
qué frecuencia aparece cada letra en el texto. Tal análisis puede ser útil en criptografía,
por lo que queremos poder hacerlo en referencia al alfabeto latino.

Tu tarea es escribir un programa que:

• Pida al usuario el nombre del archivo de entrada.


• Lea el archivo (si es posible) y cuente todas las letras latinas (las letras mayúsculas
y minúsculas se tratan como iguales).
• Imprima un histograma simple en orden alfabético (solo se deben presentar
recuentos distintos de cero).

Crea un archivo de prueba para tu código y verifica si tu histograma contiene resultados


válidos.

Suponiendo que el archivo de prueba contiene solo una línea con:

aBc
samplefile.txt

El resultado esperado debería verse de la siguiente manera:a -> 1

b -> 1
c -> 1
salida

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.

4.3.1.16 LABORATORIO: Histograma de frecuencia de caracteres


ordenado
LABORATORIO

Tiempo Estimado
15-30 minutos

Nivel de dificultad
Medio

Prerrequisitos
4.3.1.15

Objetivos
• Mejorar las habilidades del estudiante para operar con archivos en modo
(lectura/escritura).
• Emplear lambdas para cambiar el ordenamiento.

Escenario
El código anterior necesita ser mejorado. Está bien, pero tiene que ser mejor.

Tu tarea es hacer algunas enmiendas, que generen los siguientes resultados:

• El histograma de salida se ordenará en función de la frecuencia de los caracteres


(el contador más grande debe presentarse primero).
• El histograma debe enviarse a un archivo con el mismo nombre que el de entrada,
pero con la extensión '.hist' (debe concatenarse con el nombre original).

Suponiendo que el archivo de prueba contiene solo una línea con:

cBabAa
samplefile.txt

El resultado esperado debería verse de la siguiente manera:

a -> 3
b -> 2
c -> 1
salida

Tip: Emplea una lambda para cambiar el ordenamiento.

4.3.1.17 LABORATORIO: Evaluando los resultados de los estudiantes


LABORATORIO

Tiempo Estimado
60-120 minutos

Nivel de dificultad
Medio-Dificil

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.

Escenario
El profesor Jekyll dirige clases con estudiantes y regularmente toma notas en un archivo
de texto. Cada línea del archivo contiene 3 elementos: el nombre del alumno, el apellido
del alumno y el número de puntos que el alumno recibió durante ciertas clases.

Los elementos están separados con espacios en blanco. Cada estudiante puede aparecer
más de una vez dentro del archivo del profesor Jekyll.

El archivo puede tener el siguiente aspecto:

John Smith 5
Anna Boleyn 4.5
John Smith 2
Anna Boleyn 11
Andrew Cox 1.5
samplefile.txt

Tu tarea es escribir un programa que:

• Pida al usuario el nombre del archivo del profesor Jekyll.


• Lea el contenido del archivo y cuenta la suma de los puntos recibidos por cada
estudiante.
• Imprima un informe simple (pero ordenado), como este:

Andrew Cox 1.5


Anna Boleyn 15.5
John Smith 7.0

salida

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 StudentsDataException(Exception):
pass

class WrongLine(StudentsDataException):
# Escribe tu código aquí.

class FileEmpty(StudentsDataException):
# Escribe tu código aquí.

4.3.1.18 RESUMEN DE SECCIÓN

Puntos Clave
1.Para leer el contenido de un archivo, se pueden utilizar los siguientes métodos:

• read(number) : lee el número de carácteres/bytes del archivo y los retorna como


una cadena, es capaz de leer todo el archivo a la vez.
• readline() : lee una sola línea del archivo de texto.
• readlines(number) : lee el número de líneas del archivo de texto; es capaz de
leer todas las líneas a la vez.
• readinto(bytearray) : lee los bytes del archivo y llena el bytearray con ellos.

2. Para escribir contenido nuevo en un archivo, se pueden utilizar los siguientes métodos:

• write(string) : escribe una cadena a un archivo de texto.


• write(bytearray) : escribe todos los bytes de un bytearray a un archivo.

3. El método open() devuelve un objeto iterable que se puede usar para recorrer todas
las líneas del archivo dentro de un bucle for . Por ejemplo:

for line in open("file", "rt"):


print(line, end='')

El código copia el contenido del archivo a la consola, línea por línea. Nota: el stream se
cierra automáticamente cuando llega al final del archivo.
Ejercicio 1

¿Qué se espera del método readlines() cuando el stream está asociado con un archivo
vacío?

Revisar

Una lista vacía (una lista de longitud cero).

Ejercicio 2

¿Qué se pretende hacer con el siguiente código?

for line in open("file", "rt"):


for char in line:
if char.lower() not in "aeiouy ":
print(char, end='')

Revisar

Copia el contenido del archivo file hacia la consola, ignorando las vocales.

Ejercico 3

Vas a procesar un mapa de bits almacenado en un archivo llamado image.png y quieres


leer su contenido como un todo en una variable bytearray llamada image . Agrega una
línea al siguiente código para lograr este objetivo.

try:
stream = open("image.png", "rb")
# Inserta una línea aquí.
stream.close()
except IOError:
print("fallido")
else:
print("exitoso")

Revisar

image = bytearray(stream.read())

4.4.1.1 El módulo os

Introducción al módulo os
En esta sección, aprenderás sobre un módulo llamado os, que te permite interactuar con
tu sistema operativo usando Python.
Proporciona funciones que están disponibles en sistemas Unix y/o Windows. Si estás
familiarizado con la consola de comandos, verás que algunas funciones dan los mismos
resultados que los comandos disponibles en los sistemas operativos.

Un buen ejemplo de esto es la función mkdir , que te permite crear un directorio como el
comando mkdir en Unix y Windows. Si no conoces este comando, no te preocupes.

Pronto tendrás la oportunidad de aprender las funciones del módulo os, para realizar
operaciones en archivos y directorios junto con los comandos correspondientes.

Además de las operaciones de archivos y directorios, el módulo os te permite:

• Obtener información sobre el sistema operativo.


• Manejar procesos.
• Operar en streams de E/S usando descriptores de archivos.

En un momento, verás cómo obtener información básica sobre tu sistema operativo,


aunque la administración de procesos y el trabajo con descriptores de archivos no se
discutirán aquí, porque estos son temas más avanzados que requieren conocimiento de
los mecanismos del sistema operativo.

¿Listo?

4.4.1.2 El módulo os

Obtener información sobre el sistema operativo


Antes de crear tu primera estructura de directorios, verás cómo puedes obtener
información sobre el sistema operativo actual. Esto es realmente fácil porque el
módulo os proporciona una función llamada uname, que devuelve un objeto que
contiene los siguientes atributos:

• systemname: almacena el nombre del sistema operativo.


• nodename: almacena el nombre de la máquina en la red.
• release: almacena el release (actualización) del sistema operativo.
• version: almacena la versión del sistema operativo.
• machine: almacena el identificador de hardware, por ejemplo, x86_64.

Veamos cómo es en la práctica:

import os
print(os.uname())

Resultado:

posix.uname_result(sysname='Linux', nodename='192d19f04766',
release='4.4.0-164-generic', version='#192-Ubuntu SMP Fri Sep 13
12:02:50 UTC 2019', machine='x86_64')
salida

Como puedes ver, la función uname devuelve un objeto que contiene información sobre
el sistema operativo. El código anterior se ejecutó en Ubuntu 16.04.6 LTS, así que no te
sorprendas si obtienes un resultado diferente, porque depende de tu sistema operativo.

Desafortunadamente, la función uname solo funciona en algunos sistemas Unix. Si usas


Windows, puede usar la función uname en el módulo plataform, que devuelve un
resultado similar.

El módulo os te permite distinguir rápidamente el sistema operativo mediante el


atributo name, que soporta uno de los siguientes nombres:

• posix: obtendrás este nombre si usas Unix.


• nt: obtendrás este nombre si usas Windows.
• java: obtendrás este nombre si tu código está escrito en Jython.

Para Ubuntu 16.04.6 LTS, el atributo name devuelve el nombre posix:

import os
print(os.name)

Resultado:

posix
salida

NOTA: En los sistemas Unix, hay un comando llamado uname que devuelve la misma
información (si lo ejecutas con la opción -a) que la función uname.

4.4.1.3 El módulo os
Creando directorios en Python
El módulo os proporciona una función llamada mkdir, la cual, como el comando mkdir en
Unix y Windows, te permite crear un directorio. La función mkdir requiere una ruta que
puede ser relativa o absoluta. Recordemos cómo se ven ambas rutas en la práctica:

• my_first_directory: esta es una ruta relativa que creará el


directorio my_first_directory en el directorio de trabajo actual.
• ./my_first_directory: esta es una ruta relativa que apunta explícitamente al
directorio de trabajo actual. Tiene el mismo efecto que la ruta anterior.
• ../my_first_directory: esta es una ruta relativa que creará el
directorio my_first_directory en el directorio superior del directorio de trabajo
actual.
• /python/my_first_directory: esta es una ruta absoluta que creará el
directorio my_first_directory, que a su vez está en el directorio raíz de python.

Observa el código en el editor. Muestra un ejemplo de cómo crear el


directorio my_first_directory usando una ruta relativa. Esta es la variante más simple de la
ruta relativa, que consiste en pasar solo el nombre del directorio.

Si pruebas tu código aquí, generará el directorio recién


creado ['my_first_directory'] (y todo el contenido del catálogo de trabajo actual).

La función mkdir crea un directorio en la ruta especificada. Ten en cuenta que ejecutar el
programa dos veces generará un FileExistsError.

Esto significa que no podemos crear un directorio si ya existe. Además del argumento de
la ruta, la función mkdir puede tomar opcionalmente el argumento mode, que especifica
los permisos del directorio. Sin embargo, en algunos sistemas, el argumento mode se
ignora.

Para cambiar los permisos del directorio, recomendamos la función chmod, que funciona
de manera similar al comando chmod en sistemas Unix. Puedes encontrar más
información al respecto en la documentación.

En el ejemplo anterior, se usa otra función proporcionada por el


módulo os llamada listdir. La función listdir devuelve una lista que contiene los nombres
de los archivos y directorios que se encuentran en la ruta pasada como argumento.

Si no se le pasa ningún argumento, se utilizará el directorio de trabajo actual (como en el


ejemplo anterior). Es importante que el resultado de la función listdir omita las entradas
'.' y '..', que se muestran, por ejemplo, cuando se usa el comando ls -a en sistemas Unix.

NOTA: Tanto en Windows como en Unix, hay un comando llamado mkdir, que requiere
una ruta de directorio. El equivalente del código anterior que crea el
directorio my_first_directory es el comando mkdir my_first_directory.
import os

os.mkdir("my_first_directory")
print(os.listdir())

4.4.1.4 El módulo os

Creación recursiva de directorios


La función mkdir es muy útil, pero ¿qué sucede si necesitas crear otro directorio dentro
del directorio que acabas de crear? Por supuesto, puedes ir al directorio creado y crear
otro directorio dentro de él, pero afortunadamente el módulo os proporciona una
función llamada makedirs , que facilita esta tarea.

La función makedirs permite la creación recursiva de directorios, lo que significa que se


crearán todos los directorios de la ruta. Veamos el código en el editor y veamos cómo es
en la práctica.

El código debería producir el siguiente resultado:

['my_second_directory']
salida

El código crea dos directorios. El primero de ellos se crea en el directorio de trabajo


actual, mientras que el segundo en el directorio my_first_directory.

No tienes que ir al directorio my_first_directory para crear el


directorio my_second_directory, porque la función makedirs hace esto por ti. En el
ejemplo anterior, vamos al directorio my_first_directory para mostrar que el
comando makedirs crea el subdirectorio my_second_directory.

Para moverte entre directorios, puedes usar una función llamada chdir, que cambia el
directorio de trabajo actual a la ruta especificada. Como argumento, toma cualquier ruta
relativa o absoluta. En nuestro ejemplo, le pasamos el nombre del primer directorio.

NOTA: El equivalente de la función makedirs en sistemas Unix es el comando mkdir con el


indicador -p, mientras que en Windows, simplemente el comando mkdir con la ruta:

• Sistemas tipo Unix:


mkdir -p my_first_directory/my_second_directory

• Windows:
mkdir my_first_directory/my_second_directory

import os

os.makedirs("my_first_directory/my_second_directory")
os.chdir("my_first_directory")
print(os.listdir())
4.4.1.5 El módulo os

¿Dónde estoy ahora?


Ya sabes cómo crear directorios y cómo moverte entre ellos. A veces, cuando tienes una
estructura de directorio muy grande en la que navegas, es posible que no sepas en qué
directorio estás trabajando actualmente.

Como probablemente habrás adivinado, el módulo os proporciona una función que


devuelve información sobre el directorio de trabajo actual. Se llama getcwd . Mira el
código en el editor para ver cómo usarlo en la práctica.

Resultado:

.../my_first_directory
.../my_first_directory/my_second_directory
salida

En el ejemplo, creamos el directorio my_first_directory y el


directorio my_second_directory dentro de él. En el siguiente paso, cambiamos el
directorio de trabajo actual al directorio my_first_directory y luego mostramos el
directorio de trabajo actual (primera línea del resultado).

A continuación, vamos al directorio my_second_directory y nuevamente mostramos el


directorio de trabajo actual (segunda línea del resultado). Como puedes ver, la
función getcwd devuelve la ruta absoluta a los directorios.

NOTA: En sistemas tipo Unix, el equivalente de la función getcwd es el comando pwd, que
imprime el nombre del directorio de trabajo actual.

import os

os.makedirs("my_first_directory/my_second_directory")
os.chdir("my_first_directory")
print(os.getcwd())
os.chdir("my_second_directory")
print(os.getcwd())
4.4.1.6 El módulo os

Eliminando directorios en Python


El módulo os también te permite eliminar directorios. Te da la opción de borrar un solo
directorio o un directorio con sus subdirectorios. Para eliminar un solo directorio, puedes
usar una función llamada rmdir , que toma la ruta como argumento. Mira el código en el
editor.

El ejemplo anterior es realmente simple. Primero, se crea el


directorio my_first_directory y luego se elimina usando la función rmdir. La
función listdir se utiliza como prueba de que el directorio se ha eliminado correctamente.
En este caso, devuelve una lista vacía. Al eliminar un directorio, asegúrate de que exista y
esté vacío; de lo contrario, se generará una excepción.

Para eliminar un directorio y sus subdirectorios, puedes utilizar la función removedirs ,


que requiere que se especifique una ruta que contenga todos los directorios que deben
eliminarse:

import os

os.makedirs("my_first_directory/my_second_directory")
os.removedirs("my_first_directory/my_second_directory")
print(os.listdir())

Al igual que con la función rmdir, si uno de los directorios no existe o no está vacío, se
generará una excepción.

NOTA: Tanto en Windows como en Unix, hay un comando llamado rmdir, que, al igual
que la función rmdir, elimina directorios. Además, ambos sistemas tienen comandos para
eliminar un directorio y su contenido. En Unix, este es el comando rm con el indicador -r.

import os

os.mkdir("my_first_directory")
print(os.listdir())
os.rmdir("my_first_directory")
print(os.listdir())

4.4.1.7 El módulo os

La función system()
Todas las funciones presentadas en esta parte del curso pueden ser reemplazadas por
una función llamada system, que ejecuta un comando que se le pasa como una cadena.
La función system está disponible tanto en Windows como en Unix. Dependiendo del
sistema, devuelve un resultado diferente.

En Windows, devuelve el valor devuelto por el shell después de ejecutar el comando


dado, mientras que en Unix, devuelve el estado de salida del proceso.

Veamos el código en el editor y veamos cómo es en la práctica.

Resultado:

0
salida

El ejemplo anterior funcionará tanto en Windows como en Unix. En nuestro caso,


recibimos el estado de salida 0, que indica éxito en los sistemas Unix.

Esto significa que se ha creado el directorio my_first_directory. Como parte del ejercicio,
intenta enumerar el contenido del directorio donde se creó el
directorio my_first_directory.

import os

returned_value = os.system("mkdir my_first_directory")


print(returned_value)

4.4.1.8 LABORATORIO: El módulo os

LABORATORIO

Tiempo Estimado
15-30 minutos

Nivel de Dificultad
Fácil

Objetivos
• Mejorar las habilidades del estudiante para interactuar con el sistema operativo.
• Uso práctico de funciones conocidas proporcionadas por el módulo os.

Escenario
No hace falta decir que los sistemas operativos te permiten buscar archivos y directorios.
Mientras estudiabas esta parte del curso, se aprendió sobre las funciones del módulo os,
que tiene todo lo que se necesita para escribir un programa que buscará directorios en
una ubicación determinada.

Para facilitar tu tarea, hemos preparado una estructura de directorio de prueba para ti:

Tu programa debe cumplir con los siguientes requisitos:

1. Escribe una función o método llamado find que tome dos argumentos
llamados path y dir. El argumento path debe aceptar una ruta relativa o absoluta a
un directorio donde debe comenzar la búsqueda, mientras que el
argumento dir debe ser el nombre de un directorio en el que deseas encontrar la
ruta dada. Tu programa debería mostrar las rutas absolutas si encuentra un
directorio con el nombre dado.
2. La búsqueda en el directorio debe realizarse de forma recursiva. Esto significa que
la búsqueda también debe incluir todos los subdirectorios en la ruta dada.

Entrada de ejemplo:
path="./tree", dir="python"

Salida de ejemplo:
.../tree/python
.../tree/cpp/other_courses/python
.../tree/c/other_courses/Python

4.4.1.9 RESUMEN DE SECCIÓN


Puntos Claves
1. La función uname devuelve un objeto que contiene información sobre el sistema
operativo actual. El objeto tiene los siguientes atributos:

• systemname (almacena el nombre del sistema operativo)


• nodename (almacena el nombre de la máquina en la red)
• release (almacena el release (actualización) del sistema operativo)
• version (almacena la versión del sistema operativo)
• machine (almacena el identificador de hardware, por ejemplo, x86_64)

2. El atributo name disponible en el módulo os te permite distinguir el sistema operativo.


Devuelve uno de los siguientes tres valores:

• posix (obtendrás este nombre si usas Unix)


• nt (obtendrás este nombre si usas Windows)
• java (obtendrá este nombre si tu código está escrito en algo como Jython)

3. La función mkdir crea un directorio en la ruta pasada como argumento. La ruta puede
ser relativa o absoluta, por ejemplo:

import os

os.mkdir("hello") # la ruta relativa


os.mkdir("/home/python/hello") # la ruta absoluta

Nota: Si el directorio existe, una excepción FileExistsError será generada. Además de


la función mkdir , el módulo os proporciona la función makedirs , que te permite crear
recursivamente todos los directorios en una ruta.

4. El resultado de la función listdir() es una lista que contiene los nombres de los
archivos y directorios que se encuentran en la ruta pasada como argumento.

Es importante recordar que la función listdir omite las entradas '.' y '..', que se
muestran, por ejemplo, cuando se utiliza el comando ls -a en sistemas Unix. Si no se
pasa la ruta, el resultado se devolverá para el directorio de trabajo actual.

5. Para moverte entre directorios, puedes usar una función llamada chdir() , que cambia
el directorio de trabajo actual a la ruta especificada. Como argumento, toma cualquier
ruta relativa o absoluta.

Si deseas averiguar cuál es el directorio de trabajo actual, puedes usar la


función getcwd() , que devuelve la ruta actual.

6. Para eliminar un directorio, puedes usar la función rmdir() , pero para eliminar un
directorio y sus subdirectorios, emplea la función removedirs() .
7. Tanto en Unix como en Windows, puedes usar la función system , que ejecuta el
comando que se le pasa como cadena, por ejemplo:

import os

returned_value = os.system("mkdir hello")

La función system en Windows devuelve el valor devuelto por el shell después de


ejecutar el comando dado, mientras que en Unix devuelve el estado de salida del
proceso.

Ejercicio 1

¿Cuál es el resultado del siguiente fragmento si se ejecuta en Unix?


import os
print(os.name)

Revisar

posix

Ejercicio 2

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


import os

os.mkdir("hello")
print(os.listdir())

Revisar

['hello']

4.5.1.1 El módulo datetime

Introducción al módulo datetime


En esta sección, aprenderás sobre un módulo de Python llamado datetime.

Como puedes adivinar, proporciona clases para trabajar con la fecha y hora. Si crees que
no necesitas profundizar en este tema, hablemos de ejemplos del uso de la fecha y la
hora en la programación.

La fecha y la hora tienen innumerables usos y probablemente sea difícil encontrar una
aplicación de producción que no los utilice. A continuación, se muestran algunos
ejemplos:
• Registro de eventos: gracias al conocimiento de la fecha y la hora, podemos
determinar cuándo ocurre exactamente un error crítico en nuestra aplicación. Al
crear registros, puedes especificar el formato de fecha y hora.
• Seguimiento de cambios en la base de datos: a veces es necesario almacenar
información sobre cuándo se creó o modificó un registro. El módulo datetime será
perfecto para este caso.
• Validación de datos: pronto aprenderás a leer la fecha y hora actuales en Python.
Conociendo la fecha y hora actuales, podrás validar varios tipos de datos, por
ejemplo, si un cupón de descuento ingresado por un usuario en nuestra
aplicación sigue siendo válido.
• Almacenamiento de información importante: ¿te imaginas las transferencias
bancarias sin almacenar la información de cuándo se realizaron? La fecha y la
hora de ciertas acciones deben conservarse y debemos ocuparnos de ello.

La fecha y la hora se utilizan en casi todas las áreas de nuestras vidas, por lo que es
importante familiarizarse con el módulo datetime de Python. ¿Estás listo para una nueva
dosis de conocimiento?

4.5.1.2 El módulo datetime

Obtener la fecha local actual y crear objetos del tipo fecha


Una de las clases proporcionadas por el módulo datetime es una clase llamada date .
Los objetos de esta clase representan una fecha que consta de año, mes y día. Mira el
código en el editor para ver cómo se ve en la práctica y como obtener la fecha local actual
usando el método today .

Ejecuta el código para ver qué sucede.

El método today devuelve un objeto del tipo date que representa la fecha local actual.
Toma en cuenta que el objeto date tiene tres atributos: año, mes y día.

Ten cuidado, porque estos atributos son de solo lectura. Para crear un objeto date ,
debes pasar los parámetros año, mes y día de la siguiente manera:

from datetime import date


my_date = date(2019, 11, 4)
print(my_date)

Ejecuta el ejemplo para ver qué sucede.

Al crear un objeto date, toma en cuenta las siguientes restricciones:

Parámetro Restricciones

año El parámetro año debe ser mayor o igual a 1 (constante MINYEAR) y menor o
igual a 9999 (constante MAXYEAR).
mes El parámetro mes debe ser mayor o igual a 1 y menor o igual a 12.

día El parámetro día debe ser mayor o igual a 1 y menor o igual que el último día del
mes y año indicados.

Nota: Más adelante en este curso, aprenderás a cambiar el formato de fecha


predeterminado.

from datetime import date

today = date.today()

print("Hoy:", today)
print("Año:", today.year)
print("Mes:", today.month)
print("Día:", today.day)

4.5.1.3 El módulo datetime

Creación de un objeto de fecha a partir de una marca de


tiempo
La clase date nos da la capacidad de crear un objeto del tipo fecha a partir de una marca
de tiempo.

En Unix, la marca de tiempo expresa el número de segundos desde el 1 de Enero de 1970


a las 00:00:00 (UTC). Esta fecha se llama la época Unix, porque es cuando comenzó el
conteo del tiempo en los sistemas Unix.

La marca de tiempo es en realidad la diferencia entre una fecha en particular (incluida la


hora) y el 1 de enero de 1970, 00:00:00 (UTC), expresada en segundos.

Para crear un objeto de fecha a partir de una marca de tiempo, debemos pasar una
marca de tiempo Unix al método fromtimestamp .
Para este propósito, podemos usar el módulo time , que proporciona funciones
relacionadas con el tiempo. Uno de ellos es una función llamada time() , que devuelve el
número de segundos desde el 1 de enero de 1970 hasta el momento actual en forma de
número flotante. Echa un vistazo al ejemplo en el editor.

Ejecuta el código para ver el resultado.

Si ejecutas el código de muestra varias veces, podrás ver cómo se incrementa la marca de
tiempo. Vale la pena agregar que el resultado de la función time depende de la
plataforma, porque en los sistemas Unix y Windows, los segundos intercalares no se
cuentan .

Nota: En esta parte del curso también hablaremos sobre el módulo time.

from datetime import date


import time

timestamp = time.time()
print("Marca de tiempo:", timestamp)

d = date.fromtimestamp(timestamp)
print("Fecha:", d)

4.5.1.4 El módulo datetime

Creando un objeto de fecha usando el formato ISO


El módulo datetime proporciona varios métodos para crear un objeto date . Uno de
ellos es el método fromisoformat , que toma una fecha en el formato AAAA-MM-
DD compatible con el estándar ISO 8601.

El estándar ISO 8601 define cómo se representan la fecha y la hora. Se usa a menudo, por
lo que vale la pena tomarse un momento para familiarizarse con él. Echa un vistazo a la
imagen que describe los valores requeridos por el formato:

Ahora observa el código en el editor y ejecútalo.

En nuestro ejemplo, AAAA es 2019, MM es 11 (noviembre) y DD es 04 (cuarto de


noviembre).

Cuando sustituyas la fecha, asegúrate de agregar 0 antes de un mes o de un día


expresado por un número menor que 10.
Nota: El método fromisoformat ha estado disponible en Python desde la versión 3.7.

from datetime import date

d = date.fromisoformat('2019-11-04')
print(d)

4.5.1.5 El módulo datetime

El método replace()
A veces, es posible que debas reemplazar el año, el mes o el día con un valor diferente.
No puedes hacer esto con los atributos de año, mes y día porque son de solo lectura. En
este caso, puedes utilizar el método llamado replace .

Ejecuta el código en el editor.

Resultado:

1991-02-05
1992-01-16
salida

Los parámetros year, month y day son opcionales. Puedes pasar solo un parámetro al
método replace , por ejemplo, año, o los tres como en el ejemplo.

El método replace devuelve un objeto date modificado, por lo que debes recordar
asignarlo a alguna variable.

from datetime import date

d = date(1991, 2, 5)
print(d)

d = d.replace(year=1992, month=1, day=16)


print(d)

4.5.1.6 El módulo datetime

¿Que dia de la semana es?


Uno de los métodos más útiles que facilita el trabajo con fechas es el método
llamado weekday . Devuelve el día de la semana como un número entero, donde 0 es el
Lunes y 6 es el Domingo. Ejecuta el código en el editor.

Resultado:
0
salida

La clase date tiene un método similar llamado isoweekday , que también devuelve el día
de la semana como un número entero, pero 1 es Lunes y 7 es Domingo:

from datetime import date

d = date(2019, 11, 4)
print(d.isoweekday())

Resultado:

1
salida

Como puedes ver, para la misma fecha obtenemos un número entero diferente, pero
expresando el mismo día de la semana. El entero devuelto por el
método isodayweek sigue la especificación ISO 85601.

from datetime import date

d = date(2019, 11, 4)
print(d.weekday())

4.5.1.7 El módulo datetime

Creando objetos time


Ya sabes cómo presentar una fecha utilizando el objeto date . El
módulo datetime también tiene una clase que te permite presentar la hora. ¿Puedes
adivinar su nombre? Sí, se llama time :

time(hour, minute, second, microsecond, tzinfo, fold)


El constructor de la clase time acepta los siguientes parámetros opcionales:

Parámetro Restricciones

hour El párametro hour debe ser mayor o igual que 0 y menor que 23.

minute El párametro minute debe ser mayor o igual que 0 y menor que 59.

second El párametro second debe ser mayor o igual que 0 y menor que 59.

microsecond El párametro microsecond debe ser mayor o igual que 0 y menor que 1000000.

tzinfo El párametro tzinfo debe ser un objeto de la subclase tzinfo o None (por
defecto).
fold El párametro fold debe ser 0 o 1 (predeterminadamente 0).

El párametro tzinfo está asociado con las zonas horarias, mientras que fold está asociado
con el tiempo de pared. No los usaremos durante este curso, pero te recomendamos que
te familiarices con ellos.

Veamos cómo crear un objeto de tiempo en la práctica. Ejecuta el código en el editor.

Resultado:

Tiempo: 14:53:20.000001
Hora: 14
Minuto: 53
Segundo: 20
Microsegundo: 1
salida

En el ejemplo, pasamos cuatro parámetros al constructor de la


clase: hour, minute, second, and microsecond. Se puede acceder a cada uno de ellos
utilizando los atributos de clase.

Nota: Pronto te diremos cómo puedes cambiar el formato de hora predeterminado.

from datetime import time

t = time(14, 53, 20, 1)

print("Tiempo:", t)
print("Hora:", t.hour)
print("Minuto:", t.minute)
print("Segundo:", t.second)
print("Microsegundo:", t.microsecond)
4.5.1.8 El módulo time

El módulo time
Además de la clase time , la biblioteca estándar de Python ofrece un módulo
llamado time , que proporciona una función relacionada con el tiempo. Ya se tuvo la
oportunidad de aprender la función llamada time cuando se habló de la clase date .
Ahora veremos otra función útil disponible en este módulo.

Debes pasar muchas horas frente a una computadora mientras realiza este curso. A
veces puedes sentir la necesidad de tomar una siesta. ¿Por qué no? Escribamos un
programa que simule la siesta corta de un estudiante. Echa un vistazo al código en el
editor.

Resultado:

Estoy muy cansado. Tengo que echarme una siesta. Hasta luego.
¡Dormí bien! ¡Me siento genial!
salida

La parte más importante del código de muestra es el uso de la función sleep (sí, puedes
recordarla de una de las prácticas de laboratorio anteriores en el curso), que suspende la
ejecución del programa por el determinado número de segundos.

En nuestro ejemplo, son 5 segundos. Tienes razón, es una siesta muy corta.

Extiende el sueño del estudiante cambiando la cantidad de segundos. Toma en cuenta


que la función sleep acepta solo un número entero o de punto flotante.

import time

class Student:
def take_nap(self, seconds):
print("Estoy muy cansado. Tengo que echarme una siesta. Hasta luego.")
time.sleep(seconds)
print("¡Dormí bien! ¡Me siento genial!")

student = Student()
student.take_nap(5)

4.5.1.9 El módulo time

La función ctime()
El módulo time proporciona una función llamada ctime , que convierte el tiempo en
segundos desde el 1 de enero de 1970 (época Unix) en una cadena.

¿Recuerdas el resultado de la función time ? Eso es lo que necesitas pasar a ctime . Echa
un vistazo al ejemplo en el editor.
Resultado:

Mon Nov 4 14:53:00 2019


salida

La función ctime devuelve una cadena para la marca de tiempo pasada. En nuestro
ejemplo, la marca de tiempo expresa el 4 de noviembre de 2019 a las 14:53:00.

También es posible llamar a la función ctime sin especificar el tiempo en segundos. En


este caso, se devolverá la hora actual:

import time
print(time.ctime())

import time

timestamp = 1572879180
print(time.ctime(timestamp))

4.5.1.10 El módulo time

Las funciones gmtime() y localtime()


Algunas de las funciones disponibles en el módulo time requieren conocimiento de la
clase struct_time, pero antes de conocerlas, veamos cómo se ve la clase:

time.struct_time:
tm_year # Especifica el año.
tm_mon # Especifica el mes (valor de 1 a 12)
tm_mday # Especifica el día del mes (valor de 1 a 31)
tm_hour # Especifica la hora (valor de 0 a 23)
tm_min # Especifica el minuto (valor de 0 a 59)
tm_sec # Especifica el segundo (valor de 0 a 61)
tm_wday # Especifica el día de la semana (valor de 0 a 6)
tm_yday # Especifica el día del año (valor de 1 a 366)
tm_isdst # Especifica si se aplica el horario de verano (1: sí, 0: no, -1: no se sabe)
tm_zone # Especifica el nombre de la zona horaria (valor en forma abreviada)
tm_gmtoff # Especifica el desplazamiento al este del UTC (valor en segundos)

La clase struct_time también permite el acceso a valores usando índices. El


índice 0 devuelve el valor en tm_year, mientras que 8 devuelve el valor en tm_isdst.

Las excepciones son tm_zone y tm_gmoff, a las que no se puede acceder mediante
índices. Veamos cómo usar la clase struct_time en la práctica. Ejecuta el código en el
editor.

Resultado:

time.struct_time(tm_year=2019, tm_mon=11, tm_mday=4, tm_hour=14,


tm_min=53, tm_sec=0, tm_wday=0, tm_yday=308, tm_isdst=0)
time.struct_time(tm_year=2019, tm_mon=11, tm_mday=4, tm_hour=14,
tm_min=53, tm_sec=0, tm_wday=0, tm_yday=308, tm_isdst=0)
salida

El ejemplo muestra dos funciones que convierten el tiempo transcurrido desde la época
Unix al objeto struct_time. La diferencia entre ellos es que la función gmtime devuelve el
objeto struct_time en UTC, mientras que la función localtime devuelve la hora local.
Para la función gmtime , el atributo tm_isdst es siempre 0.

import time

timestamp = 1572879180
print(time.gmtime(timestamp))
print(time.localtime(timestamp))

4.5.1.11 El módulo time

Las funciones asctime() y mktime()


El módulo time tiene funciones que esperan un objeto struct_time o una tupla que
almacena valores de acuerdo con los índices presentados cuando se habla de la
clase struct_time. Ejecuta el ejemplo en el editor.

Resultado:

Mon Nov 4 14:53:00 2019


1572879180.0
salida

La primera de las funciones, llamada asctime , convierte un objeto struct_time o una


tupla en una cadena. Toma en cuenta que la conocida función gmtime se usa para
obtener el objeto struct_time. Si no se proporciona un argumento a la función asctime ,
se utilizará el tiempo devuelto por la función localtime .

La segunda función llamada mktime convierte un objeto struct_time o una tupla que
expresa la hora local al número de segundos desde la época de Unix. En nuestro ejemplo,
le pasamos una tupla, que consta de los siguientes valores:

2019 => tm_year


11 => tm_mon
4 => tm_mday
14 => tm_hour
53 => tm_min
0 => tm_sec
0 => tm_wday
308 => tm_yday
0 => tm_isdst

import time

timestamp = 1572879180
st = time.gmtime(timestamp)
print(time.asctime(st))
print(time.mktime((2019, 11, 4, 14, 53, 0, 0, 308, 0)))

4.5.1.12 Los módulos datetime y time (continuación)

Creación de objetos datetime


En el módulo datetime , la fecha y la hora se pueden representar como objetos
separados o como un solo objeto. La clase que combina fecha y hora se llama datetime .

datetime(year, month, day, hour, minute, second, microsecond, tzinfo, fold)

Su constructor acepta los siguientes parámetros:

Parámetro Restricciones

year El parámetro year debe ser mayor o igual a 1 (constante MINYEAR) y menor o
igual a 9999 (constante MAXYEAR).
month El parámetro month debe ser mayor o igual a 1 y menor o igual a 12.

day El parámetro day debe ser mayor o igual a 1 y menor o igual al último día del
mes y año indicados.
hour El parámetro hour debe ser mayor o igual que 0 y menor que 23.

minute El parámetro minute debe ser mayor o igual que 0 y menor que 59.

second El parámetro second debe ser mayor o igual que 0 y menor que 59.

microsecond El parámetro microsecond debe ser mayor o igual que 0 y menor que 1000000.

tzinfo El parámetro tzinfo debe ser una subclase del objeto tzinfo o None (de manera
predeterminada).
fold El parámetro fold debe ser 0 o 1 (predeterminadamente 0).

Ahora echemos un vistazo al código en el editor para ver cómo creamos un objeto del
tipo datetime.

Resultado:

Fecha y Hora: 2019-11-04 14:53:00


Fecha: 2019-11-04
Hora: 14:53:00
salida

El ejemplo crea un objeto datetime que representa el 4 de noviembre de 2019 a las


14:53:00. Todos los parámetros pasados al constructor van a atributos de clase de solo
lectura. Son year, month, day, hour, minute, second, microsecond, tzinfo, y fold.
El ejemplo muestra dos métodos que devuelven dos objetos diferentes. El método
llamado date devuelve el objeto date con el año, mes y día dados, mientras que el
método llamado time devuelve el objeto time con la hora y minuto dados.

from datetime import datetime

dt = datetime(2019, 11, 4, 14, 53)

print("Fecha y Hora:", dt)


print("Fecha:", dt.date())
print("Hora:", dt.time())

4.5.1.13 Los módulos datetime y time (continuación)

Métodos que devuelven la fecha y hora actuales


La clase datetime tiene varios métodos que devuelven la fecha y hora actuales. Estos
métodos son:

• today() : devuelve la fecha y hora local actual con el atributo tzinfo establecido
a None.
• now() : devuelve la fecha y hora local actual igual que el método today, a menos
que le pasemos el argumento opcional tz. El argumento de este método debe ser
un objeto de la subclase tzinfo.
• utcnow() : devuelve la fecha y hora UTC actual con el atributo tzinfo establecido
a None.

Ejecuta el código en el editor para verlos todos en la práctica. ¿Qué puedes decir sobre la
salida?

Como puedes ver, el resultado de los tres métodos es el mismo. Las pequeñas diferencias
se deben al tiempo transcurrido entre llamadas posteriores.

Nota: Puedes leer más sobre los objetos tzinfo en la documentación.

from datetime import datetime

print("hoy:", datetime.today())
print("ahora:", datetime.now())
print("utc_ahora:", datetime.utcnow())

4.5.1.14 Los módulos datetime y time (continuación)


El obtener una marca de tiempo
Existen muchos convertidores disponibles en Internet que pueden calcular una marca de
tiempo en función de una fecha y hora determinadas, pero ¿cómo podemos hacerlo en el
módulo datetime ?

Esto es posible gracias al método timestamp proporcionado por la clase datetime .


Observa el código en el editor.

Resultado:

Timestamp: 1601823300.0
salida

El método timestamp devuelve un valor flotante que expresa el número de segundos


transcurridos entre la fecha y la hora indicadas por el objeto datetime y el 1 de enero de
1970, 00:00:00 (UTC).

from datetime import datetime

dt = datetime(2020, 10, 4, 14, 55)


print("Marca de tiempo:", dt.timestamp())

4.5.1.15 Los módulos datetime y time (continuación)

Formato de fecha y hora (parte 1)


Todas las clases del módulo datetime presentadas hasta ahora tienen un método
llamado strftime . Este es un método muy importante, porque nos permite devolver la
fecha y la hora en el formato que especificamos.

El método strftime toma solo un argumento en forma de cadena que especifica un


formato que puede constar de directivas.

Una directiva es una cadena que consta del carácter % (porcentaje) y una letra minúscula
o mayúscula. Por ejemplo, la directiva %Y significa el año con el siglo como número
decimal. Veámoslo en un ejemplo. Ejecuta el código en el editor.

Resultado:

2020/01/04
salida

En el ejemplo, hemos pasado un formato que consta de tres directivas separadas


por / (diagonal) al método strftime . Por supuesto, el carácter separador se puede
reemplazar por otro carácter, o incluso por una cadena.
Puedes poner cualquier carácter en el formato, pero solo las directivas reconocibles se
reemplazarán con los valores apropiados. En nuestro formato, hemos utilizado las
siguientes directivas:

• %Y : devuelve el año con el siglo como número decimal. En nuestro ejemplo, esto
es 2020.
• %m : devuelve el mes como un número decimal con relleno de ceros. En nuestro
ejemplo, es 01.
• %d : devuelve el día como un número decimal con relleno de ceros. En nuestro
ejemplo, es 04.

Nota: Puedes encontrar todas las directivas disponibles: aquí.

from datetime import date

d = date(2020, 1, 4)
print(d.strftime('%Y/%m/%d'))

4.5.1.16 Los módulos datetime y time (continuación)

Formato de fecha y hora (parte 2)


El formato de hora funciona de la misma forma que el formato de fecha, pero requiere el
uso de directivas adecuadas. Echemos un vistazo más de cerca a algunos de ellos en el
editor.

Resultado:

14:53:00
20/November/04 14:53:00
salida

El primero de los formatos utilizados se refiere solo al tiempo. Como puedes


adivinar, %H devuelve la hora como un número decimal con relleno de ceros, %M devuelve
el minuto como un número decimal con relleno de ceros, mientras que %S devuelve el
segundo como un número decimal con relleno de ceros. En nuestro ejemplo, %H se
reemplaza por 14, %M por 53 y %S por 00.

El segundo formato utilizado combina directivas de fecha y hora. Hay dos nuevas
directivas, %Y y %B . La directiva %Y devuelve el año sin siglo como un número decimal con
relleno de ceros (en nuestro ejemplo es 20). La directiva %B devuelve el mes como el
nombre completo.

En general, tienes mucha libertad para crear formatos, pero debes recordar usar las
directivas correctamente. Como ejercicio, puedes comprobar qué sucede si, por ejemplo,
intentas utilizar la directiva %Y en el formato pasado al método strftime del objeto time.
Intenta averiguar por qué se obtuvo este resultado. ¡Buena suerte!
from datetime import time
from datetime import datetime

t = time(14, 53)
print(t.strftime("%H:%M:%S"))

dt = datetime(2020, 11, 4, 14, 53)


print(dt.strftime("%y/%B/%d %H:%M:%S"))

4.5.1.17 Los módulos datetime y time (continuación)

La función strftime() en el módulo time


Probablemente no te sorprendas al saber que la función strftime está disponible en el
módulo time . Se diferencia ligeramente de los métodos strftime en las clases
proporcionadas por el módulo datetime porque, además del argumento de formato,
también puede tomar (opcionalmente) un objeto tuple o struct_time.

Si no se pasa una tupla o un objeto struct_time, el formateo se realizará utilizando la hora


local actual. Echa un vistazo al ejemplo en el editor.

Nuestro resultado es el siguiente:

2019/11/04 14:53:00
2020/10/12 12:19:40
salida de muestra

La creación de un formato tiene el mismo aspecto que para los métodos strftime en el
módulo datetime . En nuestro ejemplo, usamos %Y , %m , %d , %H , %M y %S directivas que ya
conoces.

En la primera llamada a la función, formateamos el objeto struct_time, mientras que en la


segunda llamada (sin el argumento opcional), formateamos la hora local. Puede
encontrar todas las directivas disponibles en el módulo time aquí.

import time

timestamp = 1572879180
st = time.gmtime(timestamp)

print(time.strftime("%Y/%m/%d %H:%M:%S", st))


print(time.strftime("%Y/%m/%d %H:%M:%S"))

4.5.1.18 Los módulos datetime y time (continuación)


El método strptime()
Saber cómo crear un formato puede ser útil cuando se usa un método
llamado strptime en la clase datetime . A diferencia del método strftime , crea un
objeto datetime a partir de una cadena que representa una fecha y una hora.

El método strptime requiere que especifiques el formato en el que guardaste la fecha y


la hora. Veámoslo en un ejemplo. Echa un vistazo al código en el editor.

Resultado:

2019-11-04 14:53:00
salida

En el ejemplo, hemos especificado dos argumentos obligatorios. El primero es una fecha


y hora como una cadena: "2019/11/04 14:53:00" , mientras que el segundo es un
formato que facilita el análisis a un objeto datetime . Ten cuidado, porque si el formato
que se especifica no coincide con la fecha y la hora en la cadena, generará un
excepción ValueError.

Nota: En el módulo time , puedes encontrar una función llamada strptime , que analiza
una cadena que representa un tiempo en un objeto struct_time. Su uso es análogo al
método strptime en la clase datetime :

import time
print(time.strptime("2019/11/04 14:53:00", "%Y/%m/%d %H:%M:%S"))

Su resultado será el siguiente:

time.struct_time(tm_year=2019, tm_mon=11, tm_mday=4, tm_hour=14,


tm_min=53, tm_sec=0, tm_wday=0, tm_yday=308, tm_isdst=-1)
salida

from datetime import datetime


print(datetime.strptime("2019/11/04 14:53:00", "%Y/%m/%d %H:%M:%S"))

4.5.1.19 Los módulos datetime y time (continuación)

Operaciones de fecha y hora


Tarde o temprano tendrás que realizar algunos cálculos sobre la fecha y la hora.
Afortunadamente, existe una clase llamada timedelta en el módulo datetime que se
creó con tal propósito.

Para crear un objeto timedelta , simplemente realiza una resta en los


objetos date o datetime , tal como hicimos en el ejemplo en el editor. Ejecútalo.
Resultado:

366 days, 0:00:00


365 days, 9:07:00
salida

El ejemplo muestra la resta para los objetos date y datetime . En el primer caso,
recibimos la diferencia en días, que es de 366 días. Toma en cuenta que también se
muestra la diferencia en horas, minutos y segundos. En el segundo caso, recibimos un
resultado diferente, porque especificamos el tiempo que se incluyó en los cálculos. Como
resultado, recibimos 365 días, 9 horas y 7 minutos.

En un momento aprenderás más sobre la creación de los objetos timedelta y sobre las
operaciones que puedes realizar con ellos.

from datetime import date


from datetime import datetime

d1 = date(2020, 11, 4)
d2 = date(2019, 11, 4)

print(d1 - d2)

dt1 = datetime(2020, 11, 4, 0, 0, 0)


dt2 = datetime(2019, 11, 4, 14, 53, 0)

print(dt1 - dt2)

4.5.1.20 Los módulos datetime y time (continuación)

Creación de objetos timedelta


Ya has aprendido que un objeto timedelta puede devolverse como resultado de restar
dos objetos date o datetime .

Por supuesto, también puedes crear un objeto tu mismo. Para ello, vamos a
familiarizarnos con los argumentos aceptados por el constructor de la clase, que
son: days , seconds , microseconds , milliseconds , minutes , hours , y weeks . Cada uno
de ellos es opcional y el valor predeterminado es 0.

Los argumentos deben ser números enteros o de punto flotante, y pueden ser positivos o
negativos. Veamos un ejemplo sencillo en el editor.

Resultado:

16 days, 3:00:00
salida
El resultado de 16 días se obtiene convirtiendo el argumento weeks en días (2 semanas =
14 días) y agregando el argumento days (2 días). Este es un comportamiento normal,
porque el objeto timedelta solo almacena días, segundos y microsegundos
internamente. De manera similar, el argumento hora se convierte en minutos. Echa un
vistazo al siguiente ejemplo:

from datetime import timedelta

delta = timedelta(weeks=2, days=2, hours=3)


print("Días:", delta.days)
print("Segundos:", delta.seconds)
print("Microsegundos:", delta.microseconds)

Resultado:

Días: 16
Segundos: 10800
Microseconds: 0
salida

El resultado de 10800 se obtiene convirtiendo 3 horas en segundos. De esta forma el


objeto timedelta almacena los argumentos pasados durante su creación. Las semanas
se convierten en días, las horas y los minutos en segundos y los milisegundos en
microsegundos.

from datetime import timedelta

delta = timedelta(weeks=2, days=2, hours=3)


print(delta)

4.5.1.21 Los módulos datetime y time (continuación)

Creación de objetos timedelta: continuación


Ya sabes cómo el objeto timedelta almacena los argumentos pasados internamente.
Veamos cómo se puede utilizar en la práctica.

Observa algunas operaciones admitidas por las clases del módulo datetime . Ejecuta el
código que te proporcionamos en el editor.

Resultado:

16 days, 2:00:00
32 days, 4:00:00
2019-11-05
2019-11-05 18:53:00
salida
El objeto timedelta se puede multiplicar por un número entero. En nuestro ejemplo,
multiplicamos el objeto que representa 16 días y 2 horas por 2. Como resultado,
recibimos un objeto timedelta que representa 32 días y 4 horas.

Toma en cuenta que tanto los días como las horas se han multiplicado por 2. Otra
operación interesante usando el objeto timedelta es la suma. En el ejemplo, hemos
sumado el objeto timedelta a los objetos date y datetime .

Como resultado de estas operaciones, recibimos objetos date y datetime incrementados


en días y horas almacenados en el objeto timedelta .

La operación de multiplicación presentada te permite aumentar rápidamente el valor del


objeto timedelta , mientras que la multiplicación también puede ayudar a obtener una
fecha en el futuro.

Por supuesto, las clases timedelta , date y datetime admiten muchas más operaciones.
Te recomendamos que te familiarices con ellos en la documentación.

from datetime import timedelta


from datetime import date
from datetime import datetime

delta = timedelta(weeks=2, days=2, hours=2)


print(delta)

delta2 = delta * 2
print(delta2)

d = date(2019, 10, 4) + delta2


print(d)

dt = datetime(2019, 10, 4, 14, 53) + delta2


print(dt)

4.5.1.22 LABORATORIO: Los módulos datetime y time

LABORATORIO

Tiempo Estimado
15-45 minutos

Nivel de Dificultad
Fácil
Objetivos
• Mejorar las habilidades del estudiante en el formato de fecha y hora.
• Mejorar las habilidades del estudiante en el uso del método strftime .

Escenario
Durante este curso, has aprendido sobre el método strftime , que requiere
conocimiento de las directivas para crear un formato. Ahora es el momento de poner en
práctica estas directivas.

Por cierto, tendrás la oportunidad de practicar el trabajo con documentación, porque


tendrás que encontrar directivas que aún no conoces.

Aquí está tu tarea:

Escribe un programa que cree un objeto datetime para el 4 de noviembre de 2020,


14:53:00. El objeto creado debe llamar al método strftime con el formato apropiado
para mostrar el siguiente resultado:

2020/11/04 14:53:00
20/November/04 14:53:00 PM
Wed, 2020 Nov 04
Wednesday, 2020 November 04
Día de la semana: 3
Día del año: 309
Número de semana en el año: 44
salida esperada

Nota: Cada línea de resultado debe crearse llamando al método strftime con al menos
una directiva en el argumento de formato.

4.5.1.23 RESUMEN DE SECCIÓN

Puntos Clave
1. Para crear un objeto date , debes pasar los argumentos de año, mes y día de la
siguiente manera:

from datetime import date

my_date = date(2020, 9, 29)


print("Año:", my_date.year) # Año: 2020
print("Mes:", my_date.month) # Mes: 9
print("Día:", my_date.day) # Día: 29

El objeto date tiene tres atributos (de solo lectura): año, mes y día.

2. El método today devuelve un objeto de fecha que representa la fecha local actual:
from datetime import date
print("Hoy:", date.today()) # Muestra: Hoy: 2020-09-29
3. En Unix, la marca de tiempo expresa el número de segundos desde el 1 de Enero de
1970 a las 00:00:00 (UTC). Esta fecha se llama la "época de Unix", porque ahí comenzó el
conteo del tiempo en los sistemas Unix. La marca de tiempo es en realidad la diferencia
entre una fecha en particular (incluida la hora) y el 1 de Enero de 1970, 00:00:00 (UTC),
expresada en segundos. Para crear un objeto de fecha a partir de una marca de tiempo,
debemos pasar una marca de tiempo Unix al método fromtimestamp :

from datetime import date


import time

timestamp = time.time()
d = date.fromtimestamp(timestamp)

Nota: La función time devuelve el número de segundos desde el 1 de Enero de 1970


hasta el momento actual en forma de número punto flotante.

4. El constructor de la clase time acepta seís argumentos


(hour, minute, second, microsecond, tzinfo, y fold). Cada uno de estos argumentos es
opcional.

from datetime import time

t = time(13, 22, 20)

print("Hora:", t.hour) # Hora: 13


print("Minuto:", t.minute) # Minuto: 22
print("Segundo:", t.second) # Segundo: 20

5. El módulo time contiene la función sleep , que suspende la ejecución del programa
durante un número determinado de segundos, por ejemplo:

import time

time.sleep(10)
print("¡Hola mundo!") # Este texto se mostrará después de 10 segundos.

6. En el módulo datetime , la fecha y la hora se pueden representar como objetos


separados o como un solo objeto. La clase que combina fecha y hora se llama datetime.
Todos los argumentos pasados al constructor van a atributos de clase de solo lectura.
Son year, month, day, hour, minute, second, microsecond, tzinfo, y fold:

from datetime import datetime

dt = datetime(2020, 9, 29, 13, 51)


print("Fecha y Hora:", dt) # Muestra: Fecha y Hora: 2020-09-29 13:51:00
7. El método strftime toma solo un argumento en forma de cadena que especifica un
formato que puede constar de directivas. Una directiva es una cadena que consta del
carácter % (porcentaje) y una letra minúscula o mayúscula. A continuación se muestran
algunas directivas útiles:

• %Y : devuelve el año con el siglo como número decimal.


• %m : devuelve el mes como un número decimal con relleno de ceros.
• %d : devuelve el día como un número decimal con relleno de ceros.
• %H : devuelve la hora como un número decimal con relleno de ceros.
• %M : devuelve el minuto como un número decimal con relleno de ceros.
• %S : devuelve el segundo como un número decimal con relleno de ceros.

Ejemplo:

from datetime import date

d = date(2020, 9, 29)
print(d.strftime('%Y/%m/%d')) # Muestra: 2020/09/29

8. Es posible realizar cálculos en los objetos date y datetime , por ejemplo:

from datetime import date

d1 = date(2020, 11, 4)
d2 = date(2019, 11, 4)

d = d1 - d2
print(d) # Muestra: 366 days, 0:00:00.
print(d * 2) # Muestra: 732 days, 0:00:00.

El resultado de la resta se devuelve como un objeto timedelta que expresa la diferencia


en días entre las dos fechas en el ejemplo anterior.

Toma en cuenta que también se muestra la diferencia en horas, minutos y segundos. El


objeto timedelta se puede utilizar para realizar más cálculos (por ejemplo, puedes
multiplicarlo por 2).

4.6.1.1 El módulo calendar

Introducción al módulo calendar


Además de los módulos datetime y time , la biblioteca estándar de Python proporciona
un módulo llamado calendar que, como su nombre indica, ofrece funciones
relacionadas con el calendario.
Uno de ellos es, por supuesto es, mostrar el calendario. Es importante que los días de la
semana se muestren de Lunes a Domingo, y cada día de la semana tiene su
representación en forma de número entero:

Día de la semana Valor entero Constante

Lunes 0 calendar.MONDAY

Martes 1 calendar.TUESDAY

Miércoles 2 calendar.WEDNESDAY

Jueves 3 calendar.THURSDAY

Viernes 4 calendar.FRIDAY

Sábado 5 calendar.SATURDAY

Domingo 6 calendar.SUNDAY

La tabla de arriba muestra la representación de los días de la semana en el


módulo calendar . El primer día de la semana (Lunes) está representado por el valor 0 y
la constante calendar.MONDAY, mientras que el último día de la semana (Domingo) está
representado por el valor 6 y la constante calendar.SUNDAY.

Para los meses, los valores enteros se indexan a partir de 1, es decir, Enero se representa
por 1 y diciembre por 12. Desafortunadamente, no hay constantes que expresen los
meses.

La información anterior te resultará útil cuando trabajes con el módulo calendar en esta
parte del curso, pero primero comencemos con algunos ejemplos sencillos de calendario.
4.6.1.2 El módulo calendar

Tu primer calendario
Comenzarás tu aventura con el módulo calendar con una función simple
llamada calendar , que te permite mostrar el calendario para todo el año. Veamos cómo
usarlo para mostrar el calendario de 2020. Ejecuta el código en el editor y ve qué sucede.

El resultado mostrado es similar al resultado del comando cal disponible en Unix. Si


deseas cambiar el formato de calendario predeterminado, puedes utilizar los siguientes
parámetros:

• w : ancho de la columna de fecha (por defecto 2)


• l : número de líneas por semana (por defecto 1)
• c : número de espacios entre las columnas del mes (por defecto 6)
• m : número de columnas (por defecto 3)

La función de calendario requiere que se especifique el año, mientras que los otros
parámetros responsables del formato son opcionales. Te recomendamos que pruebes
estos parámetros tu mismo.

Una buena alternativa a la función anterior es la función llamada prcal, que también toma
los mismos parámetros que la función calendar , pero no requiere el uso de la
función print para mostrar el calendario. Su uso se ve así:

import calendar
calendar.prcal(2020)

import calendar
print(calendar.calendar(2020))

4.6.1.3 El módulo calendar

Calendario para un mes específico


El módulo calendar tiene una función llamada month , que permite mostrar un
calendario para un mes específico. Su uso es realmente simple, solo necesita especificar
el año y el mes, consulta el código en el editor.

El ejemplo muestra el calendario de Noviembre de 2020. Al igual que en la


función calendar , puedes cambiar el formato predeterminado utilizando los siguientes
parámetros:

• w : ancho de la columna de fecha (por defecto 2)


• l : número de líneas por semana (por defecto 1)
Nota: También puedes utilizar la función prmonth , que tiene los mismos parámetros que
la función month , pero no requiere el uso de la función print para mostrar el calendario.

import calendar
print(calendar.month(2020, 11))

4.6.1.4 El módulo calendar

La función setfirstweekday()
Como ya sabes, por defecto en el módulo calendar , el primer día de la semana es el
Lunes. Sin embargo, puedes cambiar este comportamiento usando una función
llamada setfirstweekday .

¿Recuerdas la tabla que muestra los días de la semana y su representación en forma de


valores enteros? Es hora de usarla, porque el método setfirstweekday requiere un
parámetro que exprese el día de la semana en forma de valor entero. Echa un vistazo al
ejemplo en el editor.

El ejemplo usa la constante calendar.SUNDAY , que contiene un valor de 6. Por supuesto,


puedes pasar este valor directamente a la función setfirstweekday , pero la versión con
una constante es más elegante.

Como resultado, obtenemos un calendario que muestra el mes de Diciembre de 2020, en


el que el primer día de todas las semanas es el Domingo.

4.6.1.5 El módulo calendar

La función weekday()
Otra función útil proporcionada por el módulo calendar es la función llamada weekday ,
que devuelve el día de la semana como un valor entero para el año, mes y día. Veámoslo
en la práctica.

Ejecuta el código en el editor para verificar el día de la semana en que cae el 24 de


Diciembre de 2020.

Resultado:

3
salida

La función weekday devuelve 3, lo que significa que el 24 de Diciembre del 2020 es


Jueves.
import calendar
print(calendar.weekday(2020, 12, 24))

4.6.1.6 El módulo calendar

La función weekheader()
Probablemente hayas notado que el calendario contiene encabezados semanales en
forma abreviada. Si es necesario, puedes obtener nombres cortos de días de la semana
utilizando el método weekheader .

El método weekheader requiere que se especifique el ancho en caracteres para un día de


la semana. Si el ancho que se proporciona es mayor que 3, aún obtendrás los nombres
abreviados de los días de la semana que constan de tres caracteres.

Entonces, veamos cómo obtener un encabezado más pequeño. Ejecuta el código en el


editor.

Resultado:

Mo Tu We Th Fr Sa Su
salida

Nota: Si cambias el primer día de la semana, por ejemplo, usando la


función setfirstweekday , afectará el resultado de la función weekheader .

import calendar
print(calendar.weekheader(2))

4.6.1.7 El módulo calendar

¿Cómo comprobamos si un año es bisiesto?


El módulo calendar proporciona dos funciones útiles para comprobar si los años son
bisiestos.
La primera, llamada isleap , devuelve True si el año pasado es bisiesto, o False de lo
contrario. El segundo, llamado leapdays , devuelve el número de años bisiestos en el
rango de años dado.

Ejecuta el código en el editor.

Resultado:

True
3
salida

En el ejemplo, obtenemos el resultado 3, porque en el período de 2010 a 2020 solo hay


tres años bisiestos (nota: 2021 no está incluido). Son los años 2012, 2016 y 2020.

import calendar

print(calendar.isleap(2020))
print(calendar.leapdays(2010, 2021)) # Hasta 2021, pero sin incluirlo.

4.6.1.8 El módulo calendar

Clases para crear calendarios


Las funciones que hemos mostrado hasta ahora no son todo lo que ofrece el
módulo calendar . Además de ellos, podemos utilizar las siguientes clases:

• calendar.Calendar : proporciona métodos para preparar datos de calendario y


dar formato.
• calendar.TextCalendar : se utiliza para crear calendarios de texto regulares.
• calendar.HTMLCalendar : se utiliza para crear calendarios HTML.
• calendar.LocalTextCalendar : es una subclase de la
clase calendar.TextCalendar . El constructor de esta clase toma el
parámetro locale, el cual se utiliza para devolver los nombres apropiados de los
meses y días de la semana.
• calendar.LocalHTMLCalendar : es una subclase de la
clase calendar.HTMLCalendar . El constructor de esta clase toma el parámetro
"locale", que se usa para devolver los nombres apropiados de los meses y días de
la semana.

Durante este curso, ya tuviste la oportunidad de crear calendarios de texto al discutir las
funciones del módulo calendar .

Es hora de probar algo nuevo. Echemos un vistazo más de cerca a los métodos de la
clase calendar .
4.6.1.9 El módulo calendar

Creación de un objeto Calendar


El constructor de la clase Calendar toma un parámetro opcional llamado firstweekday ,
por defecto es igual a 0 (Lunes).

El parámetro firstweekday debe ser un valor entero entre 0-6. Para este propósito,
podemos usar las constantes ya conocidas: mira el código en el editor.

El programa generará el siguiente resultado:

6 0 1 2 3 4 5
salida

El ejemplo de código usa el método de la clase Calendar llamado iterweekdays , que


devuelve un iterador para los números de los días de la semana.

El primer valor devuelto siempre es igual al valor de la propiedad firstweekday . Debido


a que en nuestro ejemplo el primer valor devuelto es 6, significa que la semana comienza
un Domingo.

import calendar
c = calendar.Calendar(calendar.SUNDAY)

for weekday in c.iterweekdays():


print(weekday, end=" ")

4.6.1.10 El módulo calendar

El método itermonthdates()
La clase Calendar tiene varios métodos que devuelven un iterador. Uno de ellos es el
método itermonthdates , que requiere especificar el año y el mes.

Como resultado, se devuelven todos los días del mes y año especificados, así como todos
los días antes del comienzo del mes o del final del mes que son necesarios para obtener
una semana completa.

Cada día está representado por un objeto datetime.date . Echa un vistazo al ejemplo en
el editor.

El código muestra todos los días de Noviembre de 2019. Debido a que el primer día de
Noviembre de 2019 fue Viernes, los siguientes días también se devuelven para obtener la
semana completa: 28/10/2019 (Lunes) 29/10/2019 (Martes ) 30/10/2019 (Miércoles)
31/10/2019 (Jueves).

El último día de Noviembre de 2019 fue Sábado, por lo que para mantener la semana
completa, se devuelve un día más el 12/01/2019 (Viernes).

import calendar

c = calendar.Calendar()

for date in c.itermonthdates(2019, 11):


print(date, end=" ")

4.6.1.11 El módulo calendar

Otros métodos que devuelven iteradores


Otro método útil en la clase Calendar es el método llamado itermonthdates , que toma
año y mes como parámetros, y luego devuelve el iterador a los días de la semana
representados por números.

Mira el ejemplo en el editor.


Sin duda habrás notado la gran cantidad de ceros devueltos como resultado del código
de ejemplo. Estos son días fuera del intervalo de meses especificado que se agregan para
mantener la semana completa.

Los primeros cuatro ceros representan el 28/10/2019 (Lunes) el 29/10/2019 (Martes) el


30/10/2019 (Miércoles) el 31/10/2019 (Jueves). Los números restantes son días del mes,
excepto el último valor de 0, que reemplaza la fecha 12/01/2019 (Domingo).

Hay otros cuatro métodos similares en la clase Calendar que difieren en los datos
devueltos:

• itermonthdates2 : devuelve días en forma de tuplas que consisten en un número


de día del mes y un número de día de la semana.
• itermonthdates3 : devuelve días en forma de tuplas que constan de un año, un
mes y un día de los números del mes. Este método ha estado disponible desde la
versión 3.7 de Python.
• itermonthdates4 : devuelve días en forma de tuplas que constan de números de
año, mes, día del mes y día de la semana. Este método ha estado disponible
desde la versión 3.7 de Python.

Con fines de prueba, utiliza el ejemplo anterior y ve cómo se ven en la práctica los valores
de retorno de los métodos descritos.

import calendar

c = calendar.Calendar()

for iter in c.itermonthdays(2019, 11):


print(iter, end=" ")

4.6.1.12 El módulo calendar

El método monthdays2calendar()

La clase Calendar tiene varios otros métodos útiles sobre los que puedes obtener más
información en la documentación (https://docs.python.org/3/library/calendar.html).

Uno de ellos es el método monthdays2calendar , que toma el año y el mes, y luego


devuelve una lista de semanas en un mes específico. Cada semana es una tupla que
consta de números de días y números de días de la semana. Mira el código en el editor.

Toma en cuenta que los números de los días fuera del mes están representados por 0,
mientras que los números de los días de la semana son un número del 0 al 6, donde 0 es
el Lunes y 6 es el Domingo.

En un momento, este método puede resultar útil para completar una tarea de
laboratorio. ¿Estás listo?
import calendar

c = calendar.Calendar()

for data in c.monthdays2calendar(2020, 12):


print(data)

4.6.1.13 LABORATORIO: El módulo calendar

LABORATORIO
Tiempo Estimado
30-60 minutos

Nivel de Dificultad
Fácil

Objetivos
• Mejorar las habilidades del estudiante en el uso de la clase Calendar.

Escenario
Durante este curso, echamos un breve vistazo a la clase Calendar . Tu tarea ahora es
ampliar su funcionalidad con un nuevo método llamado count_weekday_in_year , que
toma un año y un día de la semana como parámetros, y luego devuelve el número de
ocurrencias de un día de la semana específico en el año.

Utiliza los siguientes consejos:

• Crea una clase llamada MyCalendar que se extiende de la clase Calendar .


• Crea el método count_weekday_in_year con los parámetros de year y weekday.
El parámetro weekday debe tener un valor entre 0 y 6, donde 0 es el Lunes y 6 es
el Domingo. El método debe devolver el número de días como un número entero.
• En tu implementación, usa el método monthdays2calendar de la clase Calendar .

Los siguientes son resultados esperados de ejemplo:

Argumentos de muestra

year=2019, weekday=0

Salida esperada

52
Argumentos de muestra

year=2000, weekday=6

Salida esperada

53

4.6.1.14 RESUMEN DE SECCIÓN

Puntos Claves
1. En el módulo calendar , los días de la semana se muestran de Lunes a Domingo. Cada
día de la semana tiene su representación en forma de número entero, donde el primer
día de la semana (Lunes) está representado por el valor 0, mientras que el último día de
la semana (Domingo) está representado por el valor 6.

2. Para mostrar un calendario de cualquier año, se emplea la función calendar con el


año pasado como argumento, por ejemplo:

import calendar
print(calendar.calendar(2020))

Nota: Una buena alternativa a la función anterior es la función llamada prcal , que
también toma los mismos parámetros que la función calendar , pero no requiere el uso
de la función print para mostrar el calendario.

3. Para mostrar un calendario de cualquier mes del año, se emplea la función month ,
pasándole el año y el mes. Por ejemplo:

import calendar
print(calendar.month(2020, 9))

Nota: También puedes usar la función prmonth , que tiene los mismos parámetros que la
función month , pero no requiere el uso de la función print para mostrar el calendario.

4. La función setfirstweekday te permite cambiar el primer día de la semana. Toma un


valor de 0 a 6, donde 0 es Domingo y 6 es Sábado.

5. El resultado de la función weekday es un día de la semana como un valor entero para


un año, mes y día determinados:

import calendar
print(calendar.weekday(2020, 9, 29)) # Esto muestra 1, que significa Martes.
6. La función weekheader devuelve los nombres de los días de la semana en forma
abreviada. El método weekheader requiere que se especifique el ancho en caracteres
para un día de la semana. Si el ancho que proporciona es mayor que 3, aún se obtendrán
los nombres abreviados de los días de la semana que constan de solo tres caracteres. Por
ejemplo:

import calendar
print(calendar.weekheader(2)) # Esto muestra: Mo Tu We Th Fr Sa Su

7. Una función muy útil disponible en el módulo calendar es la función llamada isleap ,
que, como su nombre indica, te permite comprobar si el año es bisiesto o no:

import calendar
print(calendar.isleap(2020)) # Esto muestra: True

8. Puedes crear un objeto calendar tu mismo usando la clase Calendar , que, al crear el
objeto, te permite cambiar el primer día de la semana con el parámetro
opcional firstweekday , por ejemplo:

import calendar

c = calendar.Calendar(2)

for weekday in c.iterweekdays():


print(weekday, end=" ")
# Resultado: 2 3 4 5 6 0 1

iterweekdays devuelve un iterador para los números de los días de la semana. El primer
valor devuelto siempre es igual al valor de la propiedad firstweekday .

Ejercicio 1

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

import calendar
print(calendar.weekheader(1))

Revisar

M T W T F S S

Ejercicio 2

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

import calendar
c = calendar.Calendar()

for weekday in c.iterweekdays():


print(weekday, end=" ")
Revisar

0 1 2 3 4 5 6

4.6.1.15 Finalización del Módulo

¡Felicitaciones! Has completado PF2: Módulo 4.


¡Bien hecho! Has llegado al final del Módulo 4 y has completado un paso importante en tu
educación sobre 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 4:

• Generadores e iteradores.
• Listas por comprensión.
• Las funciones lambda, map, y filter.
• Cierres.
• El trabajar con archivos (streams, procesamiento de archivos, el diagnosticar
problemas en los streams).
• Procesamiento de archivos de texto y binarios.
• Módulos STL seleccionados de Python: os, datetime, time, y calendar.

Ahora estás listo para realizar el quiz del módulo e intentar el desafío final: el Exámen del
Módulo 4, que te ayudará a evaluar lo que has aprendido hasta ahora.

¡Felicidades!
Has completado Fundamentos de Python 2

¡Bien hecho! Has llegado al final del curso Fundamentos de Python 2, y has completado
un paso importante en tu educación de programación en Python.
Ahora estás preparado para tomar el desafío final, el Examen Global de FP2, que te
ayudará a revisar la información más importante que has leído y probar las habilidades y
conocimientos que has adquirido a lo largo del curso.

Una vez completado el curso, también estás preparado para presentar el examen y
obtener la certificación PCAP - Certified Associate in Python Programming, lo que
garantiza que estás completamente familiarizado con todos los medios principales
proporcionados por Python 3 para que puedas comenzar tus propios estudios y abrirte
camino hacia una carrera como desarrollador, así como un paso más para alcanzar la
certificación PCPP1 - Certified Professional in Python Programming.

¿Estás listo?

• Obtén la certificación PCAP


Obtén la Certificación PCAP - Certified Associate in Python -
Programming

Al completar la serie de dos cursos de Fundamentos de Python, estás preparado para


tomar el exámen de certificación PCAP - Certified Associate in Python Programming.

La certificación te permite mantener tus habilidades actualizadas y demostrarlas a otros.


Te da la oportunidad de verificar, probar y validar tus habilidades de programación en
Python. Más importante aún, puede abrir las puertas a un mejor trabajo y un mejor
salario, y es un gran motivador para la superación personal y el desarrollo personal.

Impulsa tu carrera con la certificación PCAP - Certified Associate in Python Programming:


una credencial valiosa y poderosa que te ayudará a llevar tu carrera al siguiente nivel.

¿Qué hago ahora? ¡Obten la certificación!


Utiliza el código de descuento para reducir la tarifa del examen y compra tu cupón de
examen PCAP-31-03 con un 50% de descuento:

• Ve a la página OpenEDG Voucher Store.


• Agrega un cupón (voucher) PCAP-31-03 al carrito (has clic en +Add to Order)
• Has clic en Add Promo Code e ingresa tu código de descuento.
• Has clic en Next Step y proporciona la información de facturación y envío, realiza
tu pedido.
• Realiza el pago. Tu cupón se te enviará a la dirección de correo electrónico que se
proporcionó en el campo Shipping Information (Información de Envío). La factura
se enviará a la dirección de correo electrónico especificada en la sección Billing
Information (Información de Facturación).

Puedes pagar el cupón (voucher) para el examen PCAP-31-03 con las principales tarjetas
de crédito (es decir, Visa, MasterCard, American Express), tarjetas de débito o mediante
una cuenta de PayPal. También puedes solicitar una factura Proforma y realizar tu pago
mediante transferencia bancaria.

¡Regístrate para tu examen!


Puedes realizar el examen PCAP-31-03 desde la comodidad de tu hogar (a través de la
Supervisión en Línea de OnVue) o acudir a un centro de pruebas en persona (Centro de
Pruebas Pearson VUE).

Puedes registrar y programar tu exámen al:

• Visitar el sitio para candidatos OpenEDG Python Institute Candidate


Website (Canales de Entrega de exámenes: Centros de Pruebas de Pearson VUE /
Supervisión en Línea de OnVUE)
• Llamar al servicio al cliente Pearson VUE Contact Center: Customer
Service (Canales de Entrega de exámenes: Centros de Pruebas de Pearson VUE /
Supervisión en Línea de OnVUE)
• Contactar al Centro de Pruebas de Pearson VUE de tu elección (Canales de
Entrega de exámenes: Centros de Pruebas de Pearson VUE / Supervisión en Línea
de OnVUE) - Encuentra un Centro de Prueba.

El servicio de Supervisión en Línea de OnVUE está disponible las 24 horas del día, los 7
días de la semana, durante todo el año.

También podría gustarte