Aprende Python Quintero14mayV5
Aprende Python Quintero14mayV5
Core
1 Introducción 3
1.1 Hablando con la máquina . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2 Algo de historia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.3 Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2 Entornos de desarrollo 23
2.1 Thonny . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
2.2 Contexto real . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
2.3 VSCode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
3 Tipos de datos 45
3.1 Datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 1
3.2 Números . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
3.3 Cadenas de texto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
4 Control de flujo 99
4.1 Condicionales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
4.2 Bucles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
5 Estructuras de datos 139
5.1 Listas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
5.2 Tuplas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170
5.3 Diccionarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
5.4 Conjuntos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
5.5 Ficheros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
6 Modularidad 215
6.1 Funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
6.2 Objetos y Clases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
6.3 Excepciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289
i
6.4 Módulos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296
7 Procesamiento de texto 305
7.1 string . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306
8 Ciencia de datos 311
8.1 jupyter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 312
8.2 numpy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336
8.3 pandas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379
8.4 matplotlib . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 443
9 Scraping 485
9.1 requests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 486
9.2 beautifulsoup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 494
9.3 selenium . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 509
ii
Aprende Python
Aprende Python
Los ordenadores son dispositivos complejos pero están diseñados para hacer una cosa
bien: ejecutar aquello que se les indica. La cuestión es cómo indicar a un ordenador lo
que queremos que ejecute. Esas indicaciones se llaman técnicamente instrucciones y se
expresan en un lenguaje. Podríamos decir que programar consiste en escribir
instrucciones para que sean ejecutadas por un ordenador. El lenguaje que utilizamos para
ello se denomina lenguaje de programación.1
Pero aún seguimos con el problema de cómo hacer que un ordenador (o máquina)
entienda el lenguaje de programación. A priori podríamos decir que un ordenador sólo
entiende un lenguaje muy «simple» denominado código máquina. En este lenguaje se
utilizan únicamente los símbolos 0 y 1 en representación de los niveles de tensión alto y
bajo, que al fin y al cabo, son los estados que puede manejar un circuito digital. Hablamos
de sistema binario. Si tuviéramos que escribir programas de ordenador en este formato
sería una tarea ardua, pero afortunadamente se han ido creando con el tiempo lenguajes
de programación intermedios que, posteriormente, son convertidos a código máquina.
Si intentamos visualizar un programa en código máquina, únicamente obtendríamos una
secuencia de ceros y unos:
1.1.2 Ensamblador
SYS_SALIDA equ 1
section .data
msg db "Hello, World",0x0a
len equ $ - msg ;longitud de msg
3
section .text
global _start ;para el linker
_start: ;marca la entrada
mov eax, 4 ;llamada al sistema (sys_write)
(continué en la próxima página)
Aunque resulte difícil de creer, lo «único» que hace este programa es mostrar en la
pantalla de nuestro ordenador la frase «Hello, World», pero además teniendo en cuenta
que sólo funcionará para una arquitectura x86.
1.1.3 C
#include <stdio.h>
int main() {
printf("Hello, World");
return 0;
}
1.1.4 Python
Resumen libro de Sergio Delgado Quintero -4-
Si seguimos «subiendo» en esta lista de lenguajes de programación, podemos llegar
hasta Python. Se dice que es un lenguaje de más alto nivel en el sentido de que sus
instrucciones son más entendibles por un humano. Veamos cómo se escribiría el
programa «Hello, World» en el lenguaje de programación Python:
print("Hello, World")
¡Pues así de fácil! Hemos pasado de código máquina (ceros y unos) a código Python en
el que se puede entender perfectamente lo que estamos indicando al ordenador. La
pregunta que surge es: ¿cómo entiende una máquina lo que tiene que hacer si le
pasamos un programa hecho en Python (o cualquier otro lenguaje de alto nivel)? La
respuesta es un compilador.
4
1.1. Hablando con la máquina 5
Aprende Python
1.1.5 Compiladores
Los compiladores son programas que convierten un lenguaje «cualquiera» en código
máquina. Se pueden ver como traductores, permitiendo a la máquina interpretar lo que
queremos hacer.
1.3 Python
1.4
Resumen libro de Sergio Delgado Quintero -10-
Python es un lenguaje de programación de alto nivel creado a finales de los 80/principios
de los 90 por Guido van Rossum, holandés que trabajaba por aquella época en el Centro
para las Matemáticas y la Informática de los Países Bajos. Sus instrucciones están muy
cercanas al lenguaje natural en inglés y se hace hincapié en la legibilidad del código.
Toma su nombre de los Monty Python, grupo humorista de los 60 que gustaban mucho a
Guido. Python fue creado como sucesor del lenguaje ABC.1
Ventajas
• Libre y gratuito (OpenSource).
• Fácil de leer, parecido a pseudocódigo.
• Aprendizaje relativamente fácil y rápido: claro, intuitivo….
• Alto nivel.
• Alta Productividad: simple y rápido.
• Tiende a producir un buen código: orden, limpieza, elegancia, flexibilidad, …
• Multiplataforma. Portable.
• Multiparadigma: programación imperativa, orientada a objetos, funcional, …
• Interactivo, modular, dinámico.
• Librerías extensivas («pilas incluídas»).
• Gran cantidad de librerías de terceros.
• Extensible (C++, C, …) y «embebible».
• Gran comunidad, amplio soporte.
• Interpretado.
• Tipado dinámico5.
• Fuertemente tipado6.
Desventajas
• Interpretado (velocidad de ejecución, multithread vs GIL, etc.).
• Consumo de memoria.
• Errores durante la ejecución.
• Dos versiones mayores no del todo compatibles (v2 vs v3).
• Desarrollo móvil.
• Documentación a veces dispersa e incompleta.
• Varios módulos para la misma funcionalidad.
• Librerías de terceros no siempre del todo maduras.
5 Tipado dinámico significa que una variable puede cambiar de tipo durante el tiempo de
vida de un
programa. C es un lenguaje de tipado estático.
6 Fuertemente tipado significa que, de manera nativa, no podemos operar con dos
variables de tipos
distintos, a menos que realice una conversión explícita. Javascript es un lenguaje
débilmente tipado.
Resumen libro de Sergio Delgado Quintero -11-
De igual modo son muchas las empresas, instituciones y organismos que utilizan Python
en su día a día para mejorar sus sistemas de información. Veamos algunas de las más
relevantes: Existen ránkings y estudios de mercado que sitúan a Python como uno de los
lenguajes más usados y la vez, más amados dentro del mundo del desarrollo de software.
En el momento de la escritura de este documento, la última actualización del Índice
TIOBE es de febrero de 2022 en el que Python ocupaba el primer lugar de los
lenguajes de programación más usados, por delante de C y Java.
Resumen libro de Sergio Delgado Quintero -12-
12
1.3.4 CPython
Nivel avanzado
Existen múltiples implementaciones de Python según el lenguaje de programación que
se ha usado para desarrollarlo. Veamos algunas de ellas:
Implementación Lenguaje
CPython C
Jython Java
IronPython C#
Brython JavaScript
PyPy Python (JIT)
MicroPython C
• Los casos especiales no son lo suficientemente especiales como para romper las reglas.
• Sin embargo la practicidad le gana a la pureza.
• Los errores nunca deberían pasar silenciosamente.
• A menos que se silencien explícitamente.
• Frente a la ambigüedad, evitar la tentación de adivinar.
• Debería haber una, y preferiblemente solo una, manera obvia de hacerlo.
• A pesar de que esa manera no sea obvia a menos que seas Holandés.
• Ahora es mejor que nunca.
• A pesar de que nunca es muchas veces mejor que ahora mismo.
• Si la implementación es difícil de explicar, es una mala idea.
• Si la implementación es fácil de explicar, puede que sea una buena idea.
• Los espacios de nombres son una gran idea, ¡tengamos más de esos!
Ver también:
Si quieres darle un toque a tu escritorio, puedes descargar este fondo de pantalla del Zen
de Python que queda muy chulo.
Para poder utilizar Python debemos preparar nuestra máquina con las herramientas
necesarias. Este capítulo trata sobre la instalación y configuración de los elementos
adecuados para el desarrollo con el lenguaje de programación Python.
2.1 Thonny
2.1.1 Instalación
Para instalar Thonny debemos acceder a su web y descargar la aplicación para nuestro
sistema operativo. La ventaja es que está disponible tanto para Windows, Mac y Linux.
Una vez descargado el fichero lo ejecutamos y seguimos su instalación paso por paso.
Una vez terminada la instalación ya podemos lanzar la aplicación que se verá parecida a
la siguiente imagen:
Nota: Es posible que el aspecto del programa varíe ligeramente según el sistema
operativo, configuración de escritorio, versión utilizada o idioma (en mi caso está en
inglés), pero a efectos de funcionamiento no hay diferencia.
Hello, World
Lo que hemos hecho es indicarle a Python que ejecute como entrada la instrucción
print(Hello, World). La salida es el texto Hello, World que lo vemos en la siguiente
línea (ya sin el prompt >>>).
Importante: Los ficheros que contienen programas hechos en Python siempre deben
tener la extensión .py
18
Ahora ya podemos ejecutar nuestro fichero helloworld.py. Para ello pulsamos el botón
verde con triángulo blanco (en la barra de herramientas) o bien damos a la tecla F5.
Veremos que en el panel de Shell nos aparece la salida esperada. Lo que está pasando
«entre bambalinas» es que el intérprete de Python está recibiendo como entrada el
fichero que hemos creado; lo ejecuta y devuelve la salida para que Thonny nos lo muestre
en el panel correspondiente.
sesión de depuración y podemos avanzar instrucción por instrucción usando la tecla F7:
Resumen libro de Sergio Delgado Quintero -19-
19
Hemos visto que Thonny es una herramienta especialmente diseñada para el aprendizaje
de Python, integrando diferentes módulos que facilitan su gestión. Si bien lo podemos
utilizar para un desarrollo más «serio», se suele recurrir a un flujo de trabajo algo diferente
en contextos más reales.1
La forma más habitual de instalar Python (junto con sus librerías) es descargarlo e
instalarlo desde su página oficial:
• Versiones de Python para Windows
• Versiones de Python para Mac
• Versiones de Python para Linux
Para gestionar los paquetes que tenemos en nuestro sistema se utiliza la herramienta pip,
una utilidad que también se incluye en la instalación de Python. Con ella podremos
instalar,
1 Foto original de portada por SpaceX en Unsplash.
2 También llamada «vanilla installation» ya que es la que viene por defecto y no se hace
ningúna personalización.
Consejo: Para el caso de Anaconda usaríamos conda install pandas (aunque ya viene
preinstalado).
Nivel intermedio
Cuando trabajamos en distintos proyectos, no todos ellos requieren los mismos paquetes
ni siquiera la misma versión de Python. La gestión de estas situaciones no es sencilla si
únicamente instalamos paquetes y manejamos configuraciones a nivel global (a nivel de
máquina). Es por ello que surge el concepto de entornos virtuales. Como su propio
nombre indica se trata de crear distintos entornos en función de las necesidades de cada
proyecto, y esto nos permite establecer qué versión de Python usaremos y qué paquetes
instalaremos.
La manera más sencilla de crear un entorno virtual es la siguiente:
1 $ cd myproject
2 $ python -m venv --prompt myproject .venv
3 $ source .venv/bin/activate
• Línea 1: Entrar en la carpeta de nuestro proyecto.
Resumen libro de Sergio Delgado Quintero -21-
• Línea 2: Crear una carpeta .venv con los ficheros que constituyen el entorno virtual.
• Línea 3: Activar el entorno virtual. A partir de aquí todo lo que se instale quedará
dentro del entorno virtual.
Virtualenv
2.2.4 Editores
Resumen libro de Sergio Delgado Quintero -23-
Existen multitud de editores en el mercado que nos pueden servir perfectamente para
escribir
código Python. Algunos de ellos incorporan funcionalidades extra y otros simplemente nos
permiten editar ficheros. Cabe destacar aquí el concepto de Entorno de Desarrollo
Integrado, más conocido por sus siglas en inglés IDE3. Se trata de una aplicación
informática que proporciona servicios integrales para el desarrollo de software.
Podríamos decir que Thonny es un IDE de aprendizaje, pero existen muchos otros.
Veamos
un listado de editores de código que se suelen utilizar para desarrollo en Python:
• Editores generales o IDEs con soporte para Python:
– Eclipse + PyDev
– Sublime Text
– Atom 23
– GNU Emacs
– Vi-Vim
– Visual Studio (+ Python Tools)
– Visual Studio Code (+ Python Tools)
• Editores o IDEs específicos para Python:
3 Integrated Development Environment.
2.2. Contexto real 33
Aprende Python
– PyCharm
– Spyder
– Thonny
Cada editor tiene sus características (ventajas e inconvenientes). Supongo que la
preferencia
por alguno de ellos estará en base a la experiencia y a las necesidades que surjan. La
parte
buena es que hay diversidad de opciones para elegir.
Truco: Visual Studio Code también dispone de integración con Jupyter Notebooks.
2.2.6 repl.it
repl.it es un servicio web que ofrece un entorno de desarrollo integrado para
Resumen libro de Sergio Delgado Quintero -24-
programar en más de 50 lenguajes (Python incluido).
Es gratuito y de uso colaborativo. Se requiere una cuenta en el sistema para utilizarlo. El
hecho de no requerir instalación ni configuración previa lo hace atractivo en determinadas
circunstancias.
En su versión gratuita ofrece:
4 Término inglés utilizado para hacer referencia a algoritmos de aprendizaje automático.
5 Proceso específico para un lenguaje de programación que ejecuta instrucciones y actúa
como interfaz de entrada/salida.
24
Figura 4: repl.it
• Almacenamiento de 500MB.
• Python 3.8.2 (febrero de 2022).
• 117 paquetes preinstalados (febrero de 2022).
• Navegador (y subida) de ficheros integrado.
• Gestor de paquetes integrado.
• Integración con GitHub.
• Gestión de secretos (datos sensibles).
• Base de datos clave-valor ya integrada.
• Acceso (limitado) al sistema operativo y sistema de ficheros.
2.2.7 WSL
Si estamos trabajando en un sistema Windows 10 es posible que nos encontremos más
cómodos usando una terminal tipo «Linux», entre otras cosas para poder usar con
facilidad
las herramientas vistas en esta sección y preparar el entorno de desarrollo Python.
Durante
mucho tiempo esto fue difícil de conseguir hasta que Microsoft sacó WSL.
WSL6 nos proporciona una consola con entorno Linux que podemos utilizar en nuestro
Windows 10 sin necesidad de instalar una máquina virtual o crear una partición para un
6 Windows Subsystem for Linux.
Linux nativo. Es importante también saber que existen dos versiones de WSL hoy en día:
WSL y WSL2. La segunda es bastante reciente (publicada a mediados de 2019), tiene
mejor rendimiento y se adhiere más al comportamiento de un Linux nativo.
Resumen libro de Sergio Delgado Quintero -25-
Para la instalación de WSL7 hay que seguir los siguientes pasos:
1. Lanzamos Powershell con permisos de administrador.
2. Activamos la característica de WSL:
$ Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-
˓→Subsystem-Linux
3. Descargamos la imagen de Ubuntu 20.04 que usaremos:
$ Invoke-WebRequest -Uri https://aka.ms/wslubuntu2004 -OutFile Ubuntu.appx -
˓→UseBasicParsing
4. Finalmente, la instalamos:
$ Add-AppxPackage .\Ubuntu.appx
En este punto, WSL debería estar instalado correctamente, y debería también aparecer
en el menú Inicio.
7 Tutorial de instalación de WSL. 25
2.3 VSCode
26
Punto de ruptura
Lanzar la depuración
Ahora ya podemos lanzar la depuración pulsando la tecla F5. Nos aparecerá el siguiente
mensaje en el que dejaremos la opción por defecto Archivo de Python y pulsamos la
tecla
:
Ahora ya se inicia el «modo depuración» y veremos una pantalla similar a la siguiente:
27
28
Seguimiento de variables
Como hemos indicado previamente, la zona de Variables ya nos informa
automáticamente
de los valores de las variables que tengamos en el contexto actual de ejecución:
29
CAPÍTULO 3
Tipos de datos
Resumen libro de Sergio Delgado Quintero -30-
Igual que en el mundo real cada objeto pertenece a una categoría, en programación
manejamos objetos que tienen asociado un tipo determinado. En este capítulo se verán
los tipos de datos básicos con los que podemos trabajar en Python.
3.1 Datos
30
Los programas están formados por código y datos. Pero a nivel interno de la memoria
del ordenador no son más que una secuencia de bits. La interpretación de estos bits
depende del lenguaje de programación, que almacena en la memoria no sólo el puro dato
sino distintos metadatos.1
Cada «trozo» de memoria contiene realmente un objeto, de ahí que se diga que en
Python
todo son objetos. Y cada objeto tiene, al menos, los siguientes campos:
• Un tipo del dato almacenado.
• Un identificador único para distinguirlo de otros objetos.
• Un valor consistente con su tipo.
3.1.1 Tipos de datos
A continuación se muestran los distintos tipos de datos que podemos encontrar en
Python, sin incluir aquellos que proveen paquetes externos:
1 Foto original de portada por Alexander Sinn en Unsplash.
46 Capítulo 3. Tipos de datos
Aprende Python
Resumen libro de Sergio Delgado Quintero -31-
31
36
AMPLIAR CONOCIMIENTOS
• Basic Data Types in Python
• Variables in Python
• Immutability in Python
3.2 Números
En esta sección veremos los tipos de datos númericos que ofrece Python centrándonos
en booleanos, enteros y flotantes.1
3.2.1 Booleanos
George Boole es considerado como uno de los fundadores del campo de las ciencias de
la computación y fue el creador del Álgebra de Boole que da lugar, entre otras estructuras
Resumen libro de Sergio Delgado Quintero -39-
algebraicas, a la Lógica binaria. En esta lógica las variables sólo pueden tomar dos
valores discretos: verdadero o falso.
El tipo de datos bool proviene de lo explicado anteriormente y admite dos posibles
valores:
1 Foto original de portada por Brett Jordan en Unsplash.
58 Capítulo 3. Tipos de datos
Aprende Python
• True que se corresponde con verdadero (y también con 1 en su representación
numérica).
• False que se corresponde con falso (y también con 0 en su representación numérica).
Veamos un ejemplo de su uso:
>>> is_opened = True
>>> is_opened 39
True
>>> has_sugar = False
>>> has_sugar
False
La primera variable is_opened está representando el hecho de que algo esté abierto, y al
tomar el valor True podemos concluir que sí. La segunda variable has_sugar nos indica si
una bebida tiene azúcar; dado que toma el valor False inferimos que no lleva azúcar.
Atención: Tal y como se explicó en este apartado, los nombres de variables son
«case-sensitive». De igual modo el tipo booleano toma valores True y False con la
primera letra en mayúsculas. De no ser así obtendríamos un error sintáctico.
>>> is_opened = true
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name true is not defined
>>> has_sugar = false
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name false is not defined
3.2. Números 59
Aprende Python
3.2.2 Enteros
Los números enteros no tienen decimales pero sí pueden contener signo y estar
expresados
en alguna base distinta de la usual (base 10).
Literales enteros
Veamos algunos ejemplos de números enteros:
>>> 8
8
>>> 0
0
>>> 08
File "<stdin>", line 1
08
^
SyntaxError: invalid token
>>> 99
99
>>> +99
99
Resumen libro de Sergio Delgado Quintero -40-
>>> -99
-99
>>> 3000000
3000000
>>> 3_000_000
3000000
Dos detalles a tener en cuenta:
• No podemos comenzar un número entero por 0.
• Python permite dividir los números enteros con guiones bajos _ para clarificar su
lectura/escritura. A efectos prácticos es como si esos guiones bajos no existieran.
Operaciones con enteros
A continuación se muestra una tabla con las distintas operaciones sobre enteros que
podemos 40
realizar en Python:
Tabla 4: Operaciones con enteros en Python
Operador Descripción Ejemplo Resultado
+ Suma 3 + 9 12
continué en la próxima página
60 Capítulo 3. Tipos de datos
Aprende Python
Tabla 4 – proviene de la página anterior
Operador Descripción Ejemplo Resultado
- Resta 6 - 2 4
* Multiplicación 5 * 5 25
/ División flotante 9 / 2 4.5
// División entera 9 // 2 4
% Módulo 9 % 4 1
** Exponenciación 2 ** 4 16
Veamos algunas pruebas de estos operadores:
>>> 2 + 8 + 4
14
>>> 4 ** 4
256
>>> 7 / 3
2.3333333333333335
>>> 7 // 3
2
>>> 6 / 0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
Es de buen estilo de programación dejar un espacio entre cada operador. Además hay
que
tener en cuenta que podemos obtener errores dependiendo de la operación (más bien de
los
operandos) que estemos utilizando, como es el caso de la división por cero.
Igualmente es importante tener en cuenta la prioridad de los distintos operadores:
Prioridad Operador
1 (mayor) ()
2 **
3 -a +a
4 * / // %
Resumen libro de Sergio Delgado Quintero -41-
5 (menor) + -
Ejemplos de prioridad de operadores:
>>> 2 ** 2 + 4 / 2
6.0
>>> 2 ** (2 + 4) / 2
32.0
(continué en la próxima página)
3.2. Números 61
Aprende Python
(proviene de la página anterior)
>>> 2 ** (2 + 4 / 2)
16.0
Asignación aumentada 41
Python nos ofrece la posibilidad de escribir una asignación aumentada mezclando la
asignación y un operador.
Figura 7:
48
Las cadenas de texto son secuencias de caracteres. También se les conoce como
«strings» y nos permiten almacenar información textual de forma muy cómoda.1
Es importante destacar que Python 3 almacena los caracteres codificados en el estándar
Unicode, lo que es una gran ventaja con respecto a versiones antiguas del lenguaje.
Además permite representar una cantidad ingente de símbolos incluyendo los famosos
emojis .
3.3.1 Creando «strings»
Para escribir una cadena de texto en Python basta con rodear los caracteres con comillas
simples6
>>> “Mi primera cadena en Python
Mi primera cadena en Python
Para incluir comillas dobles dentro de la cadena de texto no hay mayor inconveniente:
>>> Los llamados "strings" son secuencias de caracteres
Los llamados "strings" son secuencias de caracteres
1 Foto original de portada por Roman Kraft en Unsplash.
6 También es posible utilizar comillas dobles. Yo me he decantado por las comillas
simples ya que quedan
más limpias y suele ser el formato que devuelve el propio intérprete de Python.
3.3. Cadenas de texto 73
Aprende Python
Puede surgir la duda de cómo incluimos comillas simples dentro de la propia cadena de
texto. Veamos soluciones para ello:
Lista 4: Comillas simples escapadas
Resumen libro de Sergio Delgado Quintero -49-
>>> Los llamados \strings\ son secuencias de caracteres
"Los llamados strings son secuencias de caracteres"
Lista 5: Comillas simples dentro de comillas dobles
>>> "Los llamados strings son secuencias de caracteres"
"Los llamados strings son secuencias de caracteres"
En la primera opción estamos escapando las comillas simples para que no sean tratadas
como caracteres especiales. En la segunda opción estamos creando el «string» con
comillas
dobles (por fuera) para poder incluir directamente las comillas simples (por dentro).
Python
también nos ofrece esta posibilidad.
Comillas triples
Hay una forma alternativa de crear cadenas de texto utilizando comillas triples. Su uso 49
está
pensado principalmente para cadenas multilínea:
>>> poem = To be, or not to be, that is the question:
... Whether tis nobler in the mind to suffer
... The slings and arrows of outrageous fortune,
... Or to take arms against a sea of troubles
Importante: Los tres puntos ... que aparecen a la izquierda de las líneas no están
incluidos
en la cadena de texto. Es el símbolo que ofrece el intérprete de Python cuando saltamos
de
línea.
Cadena vacía
La cadena vacía es aquella que no contiene ningún carácter. Aunque a priori no lo pueda
parecer, es un recurso importante en cualquier código. Su representación en Python es la
siguiente:
>>>
74 Capítulo 3. Tipos de datos
Aprende Python
3.3.2 Conversión
Podemos crear «strings» a partir de otros tipos de datos usando la función str():
>>> str(True)
True
>>> str(10)
10
>>> str(21.7)
21.7
Para el caso contrario de convertir un «string» a un valor numérico, tenemos a disposición
las funciones ya vistas:
>>> int(10)
10
>>> float(21.7)
21.7
Pero hay que tener en cuenta un detalle. La función int() también admite la base en la
que se encuentra el número. Eso significa que podemos pasar un número, por ejemplo,
en
hexadecimal (como «string») y lo podríamos convertir a su valor entero:
>>> int(FF, 16)
255
Resumen libro de Sergio Delgado Quintero -50-
Nota: La base por defecto que utiliza int() para convertir cadenas de texto es la base
decimal.
3.3.3 Secuencias de escape
Python permite escapar el significado de algunos caracteres para conseguir otros
resultados.
Si escribimos una barra invertida \ antes del carácter en cuestión, le otorgamos un
significado
especial.
Quizás la secuencia de escape más conocida es \n que representa un salto de línea, pero
existen muchas otras:
# Salto de línea
>>> msg = Primera línea\nSegunda línea\nTercera línea
>>> print(msg) 50
Primera línea
Segunda línea
(continué en la próxima página)
3.3. Cadenas de texto 75
Aprende Python
(proviene de la página anterior)
Tercera línea
# Tabulador
>>> msg = Valor = \t40
>>> print(msg)
Valor = 40
# Comilla simple
>>> msg = Necesitamos \escapar\ la comilla simple
>>> print(msg)
Necesitamos escapar la comilla simple
# Barra invertida
>>> msg = Capítulo \\ Sección \\ Encabezado
>>> print(msg)
Capítulo \ Sección \ Encabezado
Nota: Al utilizar la función print() es cuando vemos realmente el resultado de utilizar los
caracteres escapados.
Expresiones literales
Nivel intermedio
Hay situaciones en las que nos interesa que los caracteres especiales pierdan ese
significado y
poder usarlos de otra manera. Existe un modificar de cadena que proporciona Python
para
tratar el texto en bruto. Es el llamado «raw data» y se aplica anteponiendo una r a la
cadena
de texto.
Veamos algunos ejemplos:
>>> text = abc\ndef
>>> print(text)
abc
def
>>> text = rabc\ndef
>>> print(text)
abc\ndef
>>> text = a\tb\tc
Resumen libro de Sergio Delgado Quintero -51-
>>> print(text)
abc
(continué en la próxima página)
76 Capítulo 3. Tipos de datos
Aprende Python
(proviene de la página anterior)
>>> text = ra\tb\tc
>>> print(text)
a\tb\tc
Consejo: El modificador r es muy utilizado para la escritura de expresiones
regulares.
3.3.4 Más sobre print()
Hemos estado utilizando la función print() de forma sencilla, pero admite algunos 51
parámetros interesantes:
1 >>> msg1 = ¿Sabes por qué estoy acá?
2 >>> msg2 = Porque me apasiona
3
4 >>> print(msg1, msg2)
5 ¿Sabes por qué estoy acá? Porque me apasiona
6
7 >>> print(msg1, msg2, sep=|)
8 ¿Sabes por qué estoy acá?|Porque me apasiona
9
10 >>> print(msg2, end=!!)
11 Porque me apasiona!!
Línea 4: Podemos imprimir todas las variables que queramos separándolas por comas.
Línea 7: El separador por defecto entre las variables es un espacio, podemos cambiar el
carácter que se utiliza como separador entre cadenas.
Línea 10: El carácter de final de texto es un salto de línea, podemos cambiar el carácter
que se utiliza como final de texto.
3.3.5 Leer datos desde teclado
Los programas se hacen para tener interacción con el usuario. Una de las formas de
interacción es solicitar la entrada de datos por teclado. Como muchos otros lenguajes de
programación, Python también nos ofrece la posibilidad de leer la información introducida
por teclado. Para ello se utiliza la función input():
>>> name = input(Introduzca su nombre: )
Introduzca su nombre: Sergio
(continué en la próxima página)
3.3. Cadenas de texto 77
Aprende Python
(proviene de la página anterior)
>>> name
Sergio
>>> type(name)
str
>>> age = input(Introduzca su edad: )
Introduzca su edad: 41
>>> age
41
>>> type(age)
str
Nota: La función input() siempre nos devuelve un objeto de tipo cadena de texto o str.
Resumen libro de Sergio Delgado Quintero -52-
Tenerlo muy en cuenta a la hora de trabajar con números, ya que debemos realizar una
conversión explícita.
Ejercicio
Escriba un programa en Python que lea por teclado dos números enteros y muestre por
pantalla el resultado de realizar las operaciones básicas entre ellos.
Ejemplo
• Valores de entrada 7 y 4.
• Salida esperada:
7+4=11
7-4=3
7*4=28
7/4=1.75
Consejo: Aproveche todo el potencial que ofrece print() para conseguir la salida 52
esperada.
Advertencia: Aunque está permitido, NUNCA llames input a una variable porque
destruirías la función que nos permite leer datos desde teclado. Y tampoco uses nombres
derivados como _input o input_ ya que no son nombres representativos que identifiquen
el propósito de la variable.
78 Capítulo 3. Tipos de datos
Aprende Python
3.3.6 Operaciones con «strings»
Combinar cadenas
Podemos combinar dos o más cadenas de texto utilizando el operador +:
>>> proverb1 = Cuando el río suena
>>> proverb2 = agua lleva
>>> proverb1 + proverb2
Cuando el río suenaagua lleva
>>> proverb1 + , + proverb2 # incluimos una coma
Cuando el río suena, agua lleva
Repetir cadenas
Podemos repetir dos o más cadenas de texto utilizando el operador *:
>>> reaction = Wow
>>> reaction * 4
WowWowWowWow
Obtener un carácter
Los «strings» están indexados y cada carácter tiene su propia posición. Para obtener un
único carácter dentro de una cadena de texto es necesario especificar su índice dentro de
corchetes [...].
Una tarea muy común al trabajar con cadenas de texto es dividirlas por algún tipo
de separador. En este sentido, Python nos ofrece la función split(), que debemos usar
anteponiendo el «string» que queramos dividir:
>>> proverb = No hay mal que por bien no venga
>>> proverb.split()
[No, hay, mal, que, por, bien, no, venga]
>>> tools = Martillo,Sierra,Destornillador
>>> tools.split(,)
[Martillo, Sierra, Destornillador]
Nota: Si no se especifica un separador, split() usa por defecto cualquier secuencia de
espacios en blanco, tabuladores y saltos de línea.
Aunque aún no lo hemos visto, lo que devuelve split() es una lista (otro tipo de datos en
Resumen libro de Sergio Delgado Quintero -55-
Python) donde cada elemento es una parte de la cadena de texto original:
>>> game = piedra-papel-tijera
>>> type(game.split(-))
list
Ejercicio
Sabiendo que la longitud de una lista se calcula igual que la longitud de una cadena de
texto,
obtenga el número de palabras que contiene la siguiente cadena de texto:
quote = Before software can be reusable, it first has to be usable
Existe una forma algo más avanzada de dividir una cadena a través del particionado.
Para
ello podemos valernos de la función partition() que proporciona Python.
Esta función toma un argumento como separador, y divide la cadena de texto en 3 partes: 55
lo
que queda a la izquiera del separador, el separador en sí mismo y lo que queda a la
derecha
del separador:
>>> text = 3 + 4
>>> text.partition(+)
(3 , +, 4)
3.3. Cadenas de texto 83
Aprende Python
Limpiar cadenas
Cuando leemos datos del usuario o de cualquier fuente externa de información, es
bastante
probable que se incluyan en esas cadenas de texto, caracteres de relleno3 al comienzo y
al final. Python nos ofrece la posibilidad de eliminar estos caracteres u otros que no nos
interesen.
La función strip() se utiliza para eliminar caracteres del principio y del final de un
«string». También existen variantes de esta función para aplicarla únicamente al
comienzo
o únicamente al final de la cadena de texto.
Supongamos que debemos procesar un fichero con números de serie de un determinado
artículo. Cada línea contiene el valor que nos interesa pero se han «colado» ciertos
caracteres
de relleno que debemos limpiar:
>>> serial_number = \n\t \n 48374983274832 \n\n\t \t \n
>>> serial_number.strip()
48374983274832
Nota: Si no se especifican los caracteres a eliminar, strip() usa por defecto cualquier
combinación de espacios en blanco, saltos de línea \n y tabuladores \t.
A continuación vamos a hacer «limpieza» por la izquierda (comienzo) y por la derecha
(final)
utilizando la función lstrip() y rstrip() respectivamente:
Lista 6: «Left strip»
>>> serial_number.lstrip()
48374983274832 \n\n\t \t \n
Lista 7: «Right strip»
>>> serial_number.rstrip()
\n\t \n 48374983274832
Como habíamos comentado, también existe la posibilidad de especificar los caracteres
que
Resumen libro de Sergio Delgado Quintero -56-
queremos borrar:
>>> serial_number.strip(\n)
\t \n 48374983274832 \n\n\t \t
Importante: La función strip() no modifica la cadena que estamos usando (algo obvio
3 Se suele utilizar el término inglés «padding» para referirse a estos caracteres.
84 Capítulo 3. Tipos de datos
Aprende Python
porque los «strings» son inmutables) sino que devuelve una nueva cadena de texto con
las
modificaciones pertinentes.
Realizar búsquedas
Aunque hemos visto que la forma pitónica de saber si una subcadena se encuentra dentro
de otra es a través del operador in, Python nos ofrece distintas alternativas para realizar 56
búsquedas en cadenas de texto.
Vamos a partir de una variable que contiene un trozo de la canción Mediterráneo de Joan
Manuel Serrat para ejemplificar las distintas opciones que tenemos:
>>> lyrics = Quizás porque mi niñez
... Sigue jugando en tu playa
... Y escondido tras las cañas
... Duerme mi primer amor
... Llevo tu luz y tu olor
... Por dondequiera que vaya
Comprobar si una cadena de texto empieza o termina por alguna subcadena:
>>> lyrics.startswith(Quizás)
True
>>> lyrics.endswith(Final)
False
Encontrar la primera ocurrencia de alguna subcadena:
>>> lyrics.find(amor)
93
>>> lyrics.index(amor) # Same behaviour?
93
Tanto find() como index() devuelven el índice de la primera ocurrencia de la subcadena
que estemos buscando, pero se diferencian en su comportamiento cuando la subcadena
buscada no existe:
>>> lyrics.find(universo)
-1
>>> lyrics.index(universo)
Traceback (most recent call last):
(continué en la próxima página)
3.3. Cadenas de texto 85
Aprende Python
(proviene de la página anterior)
File "<stdin>", line 1, in <module>
ValueError: substring not found
Contabilizar el número de veces que aparece una subcadena:
>>> lyrics.count(mi)
2
>>> lyrics.count(tu)
3
>>> lyrics.count(él)
0
Resumen libro de Sergio Delgado Quintero -57-
Ejercicio
Dada la siguiente letra5, obtenga la misma pero sustituyendo la palabra voices por
sounds:
>>> song = You look so beautiful in this light
... Your silhouette over me
... The way it brings out the blue in your eyes
... Is the Tenerife sea
... And all of the voices surrounding us here
... They just fade out when you take a breath
... Just say the word and I will disappear
... Into the wilderness
Utilice para ello únicamente búsqueda, concatenación y troceado de cadenas de texto.
Reemplazar elementos 57
Podemos usar la función replace() indicando la subcadena a reemplazar, la subcadena
de reemplazo y cuántas instancias se deben reemplazar. Si no se especifica este último
argumento, la sustitución se hará en todas las instancias encontradas:
>>> proverb = Quien mal anda mal acaba
>>> proverb.replace(mal, bien)
Quien bien anda bien acaba
>>> proverb.replace(mal, bien, 1) # sólo 1 reemplazo
Quien bien anda mal acaba
5 «Tenerife Sea» por Ed Sheeran.
86 Capítulo 3. Tipos de datos
Aprende Python
Mayúsculas y minúsculas
Python nos permite realizar variaciones en los caracteres de una cadena de texto para
pasarlos
a mayúsculas y/o minúsculas. Veamos las distintas opciones disponibles:
>>> proverb = quien a buen árbol se arrima Buena Sombra le cobija
>>> proverb
quien a buen árbol se arrima Buena Sombra le cobija
>>> proverb.capitalize()
Quien a buen árbol se arrima buena sombra le cobija
>>> proverb.title()
Quien A Buen Árbol Se Arrima Buena Sombra Le Cobija
>>> proverb.upper()
QUIEN A BUEN ÁRBOL SE ARRIMA BUENA SOMBRA LE COBIJA
>>> proverb.lower()
quien a buen árbol se arrima buena sombra le cobija
>>> proverb.swapcase()
QUIEN A BUEN ÁRBOL SE ARRIMA bUENA sOMBRA LE COBIJA
Identificando caracteres
Hay veces que recibimos información textual de distintas fuentes de las que necesitamos
identificar qué tipo de caracteres contienen. Para ello Python nos ofrece un grupo de
funciones.
Veamos algunas de estas funciones:
Lista 8: Detectar si todos los caracteres son letras o
números
>>> R2D2.isalnum()
True
>>> C3-PO.isalnum()
False
Resumen libro de Sergio Delgado Quintero -58-
Lista 9: Detectar si todos los caracteres son números
>>> 314.isnumeric()
True
(continué en la próxima página)
3.3. Cadenas de texto 87
Aprende Python
(proviene de la página anterior)
>>> 3.14.isnumeric()
False
Lista 10: Detectar si todos los caracteres son letras
>>> abc.isalpha()
True
>>> a-b-c.isalpha() 58
False
Lista 11: Detectar mayúsculas/minúsculas
>>> BIG.isupper()
True
>>> small.islower()
True
>>> First Heading.istitle()
True
3.3.7 Interpolación de cadenas
En este apartado veremos cómo interpolar valores dentro de cadenas de texto utilizando
diferentes formatos. Interpolar (en este contexto) significa sustituir una variable por su
valor
dentro de una cadena de texto.
Veamos los estilos que proporciona Python para este cometido:
Nombre Símbolo Soportado
Estilo antiguo % >= Python2
Estilo «nuevo» .format >= Python2.6
«f-strings» f >= Python3.6
Aunque aún podemos encontrar código con el estilo antiguo y el estilo nuevo en el
formateo
de cadenas, vamos a centrarnos en el análisis de los «f-strings» que se están utilizando
bastante en la actualidad.
88 Capítulo 3. Tipos de datos
Aprende Python
«f-strings»
Los f-strings aparecieron en Python 3.6 y se suelen usar en código de nueva creación.
Es
la forma más potente – y en muchas ocasiones más eficiente – de formar cadenas de
texto
incluyendo valores de otras variables.
La interpolación en cadenas de texto es un concepto que existe en la gran mayoría de
lenguajes de programación y hace referencia al hecho de sustituir los nombres de
variables
por sus valores cuando se construye un «string».
Para indicar en Python que una cadena es un «f-string» basta con precederla de una f e
incluir las variables o expresiones a interpolar entre llaves {...}.
Supongamos que disponemos de los datos de una persona y queremos formar una frase
de
bienvenida con ellos:
Resumen libro de Sergio Delgado Quintero -59-
>>> name = Elon Musk
>>> age = 49
>>> fortune = 43_300
>>> fMe llamo {name}, tengo {age} años y una fortuna de {fortune} millones
Me llamo Elon Musk, tengo 49 años y una fortuna de 43300 millones
Advertencia: Si olvidamos poner la f delante del «string» no conseguiremos sustitución
de variables.
Podría surgir la duda de cómo incluir llaves dentro de la cadena de texto, teniendo en
cuenta
que las llaves son símbolos especiales para la interpolación de variables. La respuesta es
duplicar las llaves:
>>> x = 10
>>> fThe variable is {{ x = {x} }} 59
The variable is { x = 10 }
Formateando cadenas
Nivel intermedio
Los «f-strings» proporcionan una gran variedad de opciones de formateado: ancho del
texto, número de decimales, tamaño de la cifra, alineación, etc. Muchas de estas
facilidades
se pueden consultar en el artículo Best of Python3.6 f-strings4
Dando formato a valores enteros:
4 Escrito por Nirant Kasliwal en Medium.
3.3. Cadenas de texto 89
Aprende Python
>>> mount_height = 3718
>>> f{mount_height:10d}
3718
>>> f{mount_height:010d}
0000003718
Dando formato a otras bases:
>>> value = 0b10010011
>>> f{value}
147
>>> f{value:b}
10010011
>>> value = 0o47622
>>> f{value}
20370
>>> f{value:o}
47622
>>> value = 0xab217
>>> f{value}
700951
>>> f{value:x}
ab217
Dando formato a valores flotantes:
>>> pi = 3.14159265
>>> f{pi:f} # 6 decimales por defecto (se rellenan con ceros si procede)
3.141593
>>> f{pi:.3f}
3.142
>>> f{pi:12f}
Resumen libro de Sergio Delgado Quintero -60-
3.141593
>>> f{pi:7.2f}
3.14
>>> f{pi:07.2f}
(continué en la próxima página)
90 Capítulo 3. Tipos de datos
Aprende Python
(proviene de la página anterior)
0003.14
>>> f{pi:.010f}
3.1415926500
>>> f{pi:e}
3.141593e+00 60
Alineando valores:
>>> text1 = how
>>> text2 = are
>>> text3 = you
>>> f{text1:<7s}|{text2:^11s}|{text3:>7s}
how | are | you
>>> f{text1:-<7s}|{text2:·^11s}|{text3:->7s}
how----|····are····|----you
Modo «debug»
A partir de Python 3.8, los «f-strings» permiten imprimir el nombre de la variable y su
valor,
como un atajo para depurar nuestro código. Para ello sólo tenemos que incluir un símbolo
=
después del nombre de la variable:
>>> serie = The Simpsons
>>> imdb_rating = 8.7
>>> num_seasons = 30
>>> f{serie=}
"serie=The Simpsons"
>>> f{imdb_rating=}
imdb_rating=8.7
>>> f{serie[4:]=} # incluso podemos añadir expresiones!
"serie[4:]=Simpsons"
>>> f{imdb_rating / num_seasons=}
imdb_rating / num_seasons=0.29
Modo «representación»
Si imprimimos el valor de una variable utilizando un «f-string», obviamente veremos ese
valor tal y como esperaríamos:
>>> name = Steven Spielberg
>>> print(f{name})
Steven Spielberg
Pero si quisiéramos ver la representación del objeto, tal y como se almacena
internamente,
podríamos utilizar el modificador !r en el «f-string»:
>>> name = Steven Spielberg
>>> print(f{name!r})
Steven Spielberg
En este caso se han añadido las comillas denotando que es una cadena de texto. Este
Resumen libro de Sergio Delgado Quintero -61-
modificador se puede aplicar a cualquier otro tipo de dato.
Ejercicio
Dada la variable:
e = 2.71828
, obtenga los siguientes resultados utilizando «f-strings»:
2.718
2.718280
2.72 # 4 espacios en blanco
2.718280e+00
00002.7183
2.71828 # 12 espacios en blanco
3.3.8 Caracteres Unicode
Python trabaja por defecto con caracteres Unicode. Eso significa que tenemos acceso a 61
la
amplia carta de caracteres que nos ofrece este estándar de codificación.
Supongamos un ejemplo sobre el típico «emoji» de un cohete definido en este cuadro:
La función chr() permite representar un carácter a partir de su código:
92 Capítulo 3. Tipos de datos
Aprende Python
Figura 9: Representación Unicode del carácter ROCKET
>>> rocket_code = 0x1F680
>>> rocket = chr(rocket_code)
>>> rocket
La función ord() permite obtener el código (decimal) de un carácter a partir de su
representación:
>>> rocket_code = hex(ord(rocket))
>>> rocket_code
0x1f680
El modificador \N permite representar un carácter a partir de su nombre:
>>> \N{ROCKET}
Ver también:
Tabla ASCII
Comparar cadenas
Cuando comparamos dos cadenas de texto lo hacemos en términos lexicográficos. Es
decir,
se van comparando los caracteres de ambas cadenas uno a uno y se va mirando cuál
está
«antes».
Por ejemplo:
>>> arca < arpa # ar es igual para ambas
True
(continué en la próxima página)
3.3. Cadenas de texto 93
Aprende Python
(proviene de la página anterior)
>>> ord(c)
99
>>> ord(p)
112
Nota: Internamente se utiliza la función ord() para comparar qué carácter está «antes».
Resumen libro de Sergio Delgado Quintero -62-
Otros ejemplos:
>>> a < antes
True
>>> antes < después
True
>>> después < ahora
False
>>> ahora < a
False
Tener en cuenta que en Python la letras mayúsculas van antes que las minúsculas:
>>> A < a
True
3.3.9 Casos de uso 62
Nivel avanzado
Hemos estado usando muchas funciones de objetos tipo «string» (y de otros tipos
previamente). Pero quizás no sabemos aún como podemos descubrir todo lo que
podemos
hacer con ellos y los casos de uso que nos ofrece.
Python proporciona una función «built-in» llamada dir() para inspeccionar un determinado
tipo de objeto:
>>> text = This is it!
>>> dir(text)
[__add__,
__class__,
__contains__,
(continué en la próxima página)
94 Capítulo 3. Tipos de datos
Aprende Python
(proviene de la página anterior)
__delattr__,
__dir__,
__doc__,
__eq__,
__format__,
__ge__,
__getattribute__,
__getitem__,
__getnewargs__,
__gt__,
__hash__,
__init__,
__init_subclass__,
__iter__,
__le__,
__len__,
__lt__,
__mod__,
__mul__,
__ne__,
__new__,
__reduce__,
__reduce_ex__,
Resumen libro de Sergio Delgado Quintero -63-
__repr__,
__rmod__,
__rmul__,
__setattr__,
__sizeof__,
__str__,
__subclasshook__,
capitalize,
casefold,
center,
count,
encode,
endswith, 63
expandtabs,
find,
format,
format_map,
index,
isalnum,
isalpha,
isascii,
isdecimal,
(continué en la próxima página)
3.3. Cadenas de texto 95
Aprende Python
(proviene de la página anterior)
isdigit,
isidentifier,
islower,
isnumeric,
isprintable,
isspace,
istitle,
isupper,
join,
ljust,
lower,
lstrip,
maketrans,
partition,
replace,
rfind,
rindex,
rjust,
rpartition,
rsplit,
rstrip,
split,
splitlines,
startswith,
strip,
swapcase,
Resumen libro de Sergio Delgado Quintero -64-
title,
translate,
upper,
zfill]
Esto es aplicable tanto a variables como a literales e incluso a tipos de datos (clases)
explícitos:
>>> dir(10)
[__abs__,
__add__,
__and__,
__bool__,
...
imag, 64
numerator,
real,
to_bytes]
(continué en la próxima página)
96 Capítulo 3. Tipos de datos
Aprende Python
(proviene de la página anterior)
>>> dir(float)
[__abs__,
__add__,
__bool__,
__class__,
...
hex,
imag,
is_integer,
real]
EJERCICIOS DE REPASO
1. Escriba un programa en Python que acepte el nombre y los apellidos de una
persona y • Entrada: name=Sergio; surname=Delgado Quintero
• Salida: Delgado Quintero, Sergio
2. Escriba un programa en Python que acepte una ruta remota de recurso samba, y
lo separe • Entrada: //1.1.1.1/eoi/python
• Salida: host=1.1.1.1; path=/eoi/python
3. Escriba un programa en Python que acepte un «string» con los 8 dígitos de un
NIF, y • Entrada: 12345678
• Salida: 12345678Z
4. Escriba un programa en Python que acepte un entero n y compute el valor de n +
nn + • Entrada: 5
• Salida: 615 (5 + 55 + 555)
5. Escriba un programa en Python que acepte una palabra en castellano y calcule
una métrica • Entrada: ordenador
• Salida: 36
3.3. Cadenas de texto 97
Aprende Python
6. Escriba un programa en Python que transforme un título HTML <hx>...</hx> en su
correspondiente • Entrada: <h3>Cadenas de texto</h3>
• Salida: ### Cadenas de texto
EJERCICIOS EXTERNOS
Resumen libro de Sergio Delgado Quintero -65-
1. If you can’t sleep, just count sheep!!
2. Remove first and last character
3. Name shuffler
4. Double char
5. Find the position!
6. Find the integral
7. Multiply the number
AMPLIAR CONOCIMIENTOS
• A Guide to the Newer Python String Format Techniques
• Strings and Character Data in Python
• How to Convert a Python String to int
• Your Guide to the Python print<> Function
• Basic Input, Output, and String Formatting in Python 65
• Unicode & Character Encodings in Python: A Painless Guide
• Python String Formatting Tips & Best Practices
• Python 3’s f-Strings: An Improved String Formatting Syntax
• Splitting, Concatenating, and Joining Strings in Python
• Conditional Statements in Python
• Python String Formatting Best Practices
Control de flujo
Todo programa informático está formado por instrucciones que se ejecutan en forma
secuencial de «arriba» a «abajo», de igual manera que leeríamos un libro. Este orden
constituye el llamado flujo del programa. Es posible modificar este flujo secuencial para
que tome bifurcaciones o repita ciertas instrucciones. Las sentencias que nos permiten
hacer estas modificaciones se engloban en el control de flujo.
4.1 Condicionales
En esta sección veremos las sentencias if y match-case junto a las distintas variantes que
pueden asumir, pero antes de eso introduciremos algunas cuestiones generales de
escritura de código.1
Resumen libro de Sergio Delgado Quintero -66-
4.1.1 Definición de bloques
A diferencia de otros lenguajes que utilizan llaves para definir los bloques de código,
cuando Guido Van Rossum creó el lenguaje quiso evitar estos caracteres por
considerarlos
innecesarios. Es por ello que en Python los bloques de código se definen a través de
espacios en blanco, preferiblemente 4.2 En términos técnicos se habla del tamaño
de indentación.
Consejo: Esto puede resultar extraño e incómodo a personas que vienen de otros
lenguajes de programación pero desaparece rápido y se siente natural a medida que se
escribe código.
1 Foto original de portada por ali nafezarefi en Unsplash.
2 Reglas de indentación definidas en PEP 8
66
4.1.2 Comentarios
Los comentarios son anotaciones que podemos incluir en nuestro programa y que nos
permiten aclarar ciertos aspectos del código. Estas indicaciones son ignoradas por el
intérprete de Python.
Los comentarios se incluyen usando el símbolo almohadilla # y comprenden hasta el final
de
la línea.
Lista 1: Comentario en bloque
# Universe age expressed in days
universe_age = 13800 * (10 ** 6) * 365
Los comentarios también pueden aparecer en la misma línea de código, aunque la guía
de
estilo de Python no aconseja usarlos en demasía:
Lista 2: Comentario en línea
stock = 0 # Release additional articles
Reglas para escribir buenos comentarios:6
1. Los comentarios no deberían duplicar el código.
2. Los buenos comentarios no arreglan un código poco claro.
3. Si no puedes escribir un comentario claro, puede haber un problema en el código.
4. Los comentarios deberían evitar la confusión, no crearla.
5. Usa comentarios para explicar código no idiomático.
6. Proporciona enlaces a la fuente original del código copiado.
6 Referencia: Best practices for writing code comments
4.1. Condicionales 101
Aprende Python
7. Incluye enlaces a referencias externas que sean de ayuda.
Resumen libro de Sergio Delgado Quintero -67-
8. Añade comentarios cuando arregles errores.
9. Usa comentarios para destacar implementaciones incompletas.
4.1.3 Ancho del código
Los programas suelen ser más legibles cuando las líneas no son excesivamente largas.
La
longitud máxima de línea recomendada por la guía de estilo de Python es de 80
caracteres.
Sin embargo, esto genera una cierta polémica hoy en día, ya que los tamaños de pantalla
han aumentado y las resoluciones son mucho mayores que hace años. Así las líneas de
más
de 80 caracteres se siguen visualizando correctamente. Hay personas que son más
estrictas
en este límite y otras más flexibles. 67
En caso de que queramos romper una línea de código demasiado larga, tenemos dos
opciones:
1. Usar la barra invertida \:
>>> factorial = 4 * 3 * 2 * 1
>>> factorial = 4 * \
... 3 * \
... 2 * \
... 1
2. Usar los paréntesis (...):
>>> factorial = 4 * 3 * 2 * 1
>>> factorial = (4 *
... 3 *
... 2 *
... 1)
4.1.4 La sentencia if
La sentencia condicional en Python (al igual que en muchos otros lenguajes de
programación)
es if. En su escritura debemos añadir una expresión de comparación terminando con
dos puntos al final de la línea. Veamos un ejemplo:
>>> temperature = 40
(continué en la próxima página)
102 Capítulo 4. Control de flujo
Aprende Python
(proviene de la página anterior)
>>> if temperature > 35:
... print(Aviso por alta temperatura)
...
Aviso por alta temperatura
Nota: Nótese que en Python no es necesario incluir paréntesis ( y ) al escribir
condiciones.
Hay veces que es recomendable por claridad o por establecer prioridades.
En el caso anterior se puede ver claramente que la condición se cumple y por tanto se
ejecuta
la instrucción que tenemos dentro del cuerpo de la condición. Pero podría no ser así. Para
controlar ese caso existe la sentencia else. Veamos el mismo ejemplo anterior pero
añadiendo
esta variante:
>>> temperature = 20
>>> if temperature > 35:
Resumen libro de Sergio Delgado Quintero -68-
... print(Aviso por alta temperatura)
... else:
... print(Parámetros normales)
...
Parámetros normales
Podríamos tener incluso condiciones dentro de condiciones, lo que se viene a llamar
técnicamente condiciones anidadas3. Veamos un ejemplo ampliando el caso anterior:
>>> temperature = 28
>>> if temperature < 20:
... if temperature < 10:
... print(Nivel azul)
... else:
... print(Nivel verde) 68
... else:
... if temperature < 30:
... print(Nivel naranja)
... else:
... print(Nivel rojo)
...
Nivel naranja
Python nos ofrece una mejora en la escritura de condiciones anidadas cuando aparecen
consecutivamente un else y un if. Podemos sustituirlos por la sentencia elif:
3 El anidamiento (o «nesting») hace referencia a incorporar sentencias unas dentro de
otras mediante la
inclusión de diversos niveles de profunidad (indentación).
71
Valor nulo
Nivel intermedio
None es un valor especial de Python que almacena el valor nulo4. Veamos cómo se
comporta
al incorporarlo en condiciones de veracidad:
>>> value = None
>>> if value:
... print(Value has some useful value)
... else:
... # value podría contener None, False (u otro)
... print(Value seems to be void)
...
Value seems to be void
Para distinguir None de los valores propiamente booleanos, se recomienda el uso del
operador
Resumen libro de Sergio Delgado Quintero -73-
is. Veamos un ejemplo en el que tratamos de averiguar si un valor es nulo:
>>> value = None
>>> if value is None:
... print(Value is clearly None)
... else:
... # value podría contener True, False (u otro)
... print(Value has some useful value)
...
Value is clearly void
De igual forma, podemos usar esta construcción para el caso contrario. La forma
«pitónica»
de preguntar si algo no es nulo es la siguiente:
>>> value = 99 73
>>> if value is not None:
... print(f{value=})
...
value=99
4 Lo que en otros lenguajes se conoce como nil, null, nothing.
4.1.8 Veracidad
Nivel intermedio
Cuando trabajamos con expresiones que incorporan valores booleanos, se produce una
conversión implícita que transforma los tipos de datos involucrados a valores True o
False.
Lo primero que debemos entender de cara comprobar la veracidad son los valores que
evalúan a falso o evalúan a verdadero.
Veamos las únicas «cosas» que son evaluadas a False en Python:
>>> bool(False)
False
>>> bool(None)
False
>>> bool(0)
False
>>> bool(0.0)
False
>>> bool() # cadena vacía
False
>>> bool([]) # lista vacía
False
>>> bool(()) # tupla vacía
False
>>> bool({}) # diccionario vacío
False
>>> bool(set()) # conjunto vacío
False
Importante: El resto de objetos son evaluados a True en Python.
Veamos algunos ejemplos que son evaluados a True en Python:
>>> bool(False)
True
(continué en la próxima página)
112 Capítulo 4. Control de flujo
Aprende Python
(proviene de la página anterior)
Resumen libro de Sergio Delgado Quintero -74-
>>> bool( )
True
>>> bool(1e-10)
True
>>> bool([0])
True
>>> bool( )
True
Asignación lógica
Es posible utilizar operadores lógicos en sentencias de asignación sacando partido de
las
tablas de la verdad que funcionan para estos casos.
Veamos un ejemplo de asignación lógica utilizando el operador or: 74
>>> b = 0
>>> c = 5
>>> a = b or c
>>> a
5
En la línea resaltada podemos ver que se está aplicando una expresión lógica, por lo
tanto
se aplica una conversión implícita de los valores enteros a valores «booleanos». En este
sentido
el valor 0 se evalúa a falso y el valor 5 se evalúa a verdadero. Como estamos en un or el
resultado será verdadero, que en este caso es el valor 5 asignado finalmente a la variable
a.
Veamos el mismo ejemplo de antes pero utilizando el operador and:
>>> b = 0
>>> c = 5
>>> a = b and c
>>> a
0
En este caso, como estamos en un and el resultado será falso, por lo que el valor 0 es
asignado finalmente a la variable a.
4.1.9 Sentencia match-case
Una de las novedades más esperadas (y quizás controvertidas) de Python 3.10 fue el
llamado Structural Pattern Matching que introdujo en el lenguaje una nueva sentencia
condicional. Ésta se podría asemejar a la sentencia «switch» que ya existe en otros
lenguajes de programación.
Comparando valores
En su versión más simple, el «pattern matching» permite comparar un valor de entrada
con una serie de literales. Algo así como un conjunto de sentencias «if» encadenadas.
Veamos esta aproximación mediante un ejemplo:
>>> color = #FF0000
>>> match color:
... case #FF0000:
... print( )
... case #00FF00:
... print( )
... case #0000FF:
... print( )
Resumen libro de Sergio Delgado Quintero -75-
...
¿Qué ocurre si el valor que comparamos no existe entre las opciones disponibles? Pues
en
principio, nada, ya que este caso no está cubierto. Si lo queremos controlar, hay que
añadir
una nueva regla utilizando el subguión _ como patrón:
>>> color = #AF549B
>>> match color:
... case #FF0000:
... print( )
... case #00FF00:
... print( )
... case #0000FF: 75
... print( )
... case _:
... print(Unknown color!)
...
Unknown color!
Ejercicio
114 Capítulo 4. Control de flujo
Aprende Python
Escriba un programa en Python que pida (por separado) dos valores numéricos y un
operando
(suma, resta, multiplicación, división) y calcule el resultado de la operación, usando para
ello
la sentencia match-case.
Controlar que la operación no sea una de las cuatro predefinidas. En este caso dar un
mensaje
de error y no mostrar resultado final.
Ejemplo
• Entrada: 4, 3, +
• Salida: 4+3=7
Patrones avanzados
Nivel avanzado
La sentencia match-case va mucho más allá de una simple comparación de valores. Con
ella
podremos deconstruir estructuras de datos, capturar elementos o mapear valores.
Para ejemplificar varias de sus funcionalidades, vamos a partir de una tupla que
representará
un punto en el plano (2 coordenadas) o en el espacio (3 coordenadas). Lo primero que
vamos
a hacer es detectar en qué dimensión se encuentra el punto:
>>> point = (2, 5)
>>> match point:
... case (x, y):
... print(f({x},{y}) is in plane)
... case (x, y, z):
... print(f({x},{y},{z}) is in space)
...
(2,5) is in plane
>>> point = (3, 1, 7)
>>> match point:
Resumen libro de Sergio Delgado Quintero -76-
... case (x, y):
... print(f({x},{y}) is in plane)
... case (x, y, z):
... print(f({x},{y},{z}) is in space)
...
(3,1,7) is in space
En cualquier caso, esta aproximación permitiría un punto formado por «strings»:
4.1. Condicionales 115
Aprende Python
>>> point = (2, 5)
>>> match point:
... case (x, y):
... print(f({x},{y}) is in plane) 76
... case (x, y, z):
... print(f({x},{y},{z}) is in space)
...
(2,5) is in plane
Por lo tanto, en un siguiente paso, podemos restringir nuestros patrones a valores
enteros:
>>> point = (2, 5)
>>> match point:
... case (int(), int()):
... print(f{point} is in plane)
... case (int(), int(), int()):
... print(f{point} is in space)
... case _:
... print(Unknown!)
...
Unknown!
>>> point = (3, 9, 1)
>>> match point:
... case (int(), int()):
... print(f{point} is in plane)
... case (int(), int(), int()):
... print(f{point} is in space)
... case _:
... print(Unknown!)
...
(3, 9, 1) is in space
Imaginemos ahora que nos piden calcular la distancia del punto al origen. Debemos tener
en
cuenta que, a priori, desconocemos si el punto está en el plano o en el espacio:
>>> point = (8, 3, 5)
>>> match point:
... case (int(x), int(y)):
... dist_to_origin = (x ** 2 + y ** 2) ** (1 / 2)
... case (int(x), int(y), int(z)):
... dist_to_origin = (x ** 2 + y ** 2 + z ** 2) ** (1 / 2)
(continué en la próxima página)
116 Capítulo 4. Control de flujo
Aprende Python
(proviene de la página anterior)
Resumen libro de Sergio Delgado Quintero -77-
... case _:
... print(Unknown!)
...
>>> dist_to_origin
9.899494936611665
Con este enfoque, nos aseguramos que los puntos de entrada deben tener todas sus
coordenadas como valores enteros:
>>> point = (8, 3, 5) # Nótese el 8 como "string"
>>> match point:
... case (int(x), int(y)):
... dist_to_origin = (x ** 2 + y ** 2) ** (1 / 2)
... case (int(x), int(y), int(z)):
... dist_to_origin = (x ** 2 + y ** 2 + z ** 2) ** (1 / 2) 77
... case _:
... print(Unknown!)
...
Unknown!
Cambiando de ejemplo, veamos un fragmento de código en el que tenemos que
comprobar
la estructura de un bloque de autenticación definido mediante un diccionario. Los
métodos válidos de autenticación son únicamente dos: bien usando nombre de usuario y
contraseña, o bien usando correo electrónico y «token» de acceso. Además, los valores
deben
venir en formato cadena de texto:
1 >>> # Lista de diccionarios
2 >>> auths = [
3 ... {username: sdelquin, password: 1234},
4 ... {email: [email protected], token: 4321},
5 ... {email: [email protected], password: ABCD},
6 ... {username: sdelquin, password: 1234}
7 ... ]
8
9 >>> for auth in auths:
10 ... print(auth)
11 ... match auth:
12 ... case {username: str(username), password: str(password)}:
13 ... print(Authenticating with username and password)
14 ... print(f{username}: {password})
15 ... case {email: str(email), token: str(token)}:
16 ... print(Authenticating with email and token)
17 ... print(f{email}: {token})
(continué en la próxima página)
4.1. Condicionales 117
Aprende Python
(proviene de la página anterior)
18 ... case _:
19 ... print(Authenticating method not valid!)
20 ... print(---)
21 ...
22 {username: sdelquin, password: 1234}
23 Authenticating with username and password
24 sdelquin: 1234
Resumen libro de Sergio Delgado Quintero -78-
25 ---
26 {email: [email protected], token: 4321}
27 Authenticating with email and token
28 [email protected]: 4321
29 ---
30 {email: [email protected], password: ABCD}
31 Authenticating method not valid!
32 ---
33 {username: sdelquin, password: 1234}
34 Authenticating method not valid!
35 ---
Cambiando de ejemplo, a continuación veremos un código que nos indica si, dada la edad
de 78
una persona, puede beber alcohol:
1 >>> age = 21
2
3 >>> match age:
4 ... case 0 | None:
5 ... print(Not a person)
6 ... case n if n < 17:
7 ... print(Nope)
8 ... case n if n < 22:
9 ... print(Not in the US)
10 ... case _:
11 ... print(Yes)
12 ...
13 Not in the US
• En la línea 4 podemos observar el uso del operador OR.
• En las líneas 6 y 8 podemos observar el uso de condiciones dando lugar a cláusulas
guarda.
EJERCICIOS EXTERNOS
1. Return the day
2. Return negative
3. What’s the real floor?
4. Area or Perimeter
5. Check same case
6. Simple multiplication
7. Quarter of the year
120 Capítulo 4. Control de flujo
Aprende Python
8. Grade book
9. Transportation on vacation
10. Safen User Input Part I - htmlspecialchars
11. Remove an exclamation mark from the end of string
12. Pythagorean triple
13. How much water do I need?
14. Set Alarm
15. Compare within margin
16. Will you make it?
17. Plural
Resumen libro de Sergio Delgado Quintero -80-
18. Student’s final grade
19. Drink about
20. Switch it up!
21. Floating point comparison
22. No zeros for heros
23. Tip calculator
24. Grader
25. Evil or Odious
26. Validate code with simple regex
27. Fuel calculator
AMPLIAR CONOCIMIENTOS
• How to Use the Python or Operator
• Conditional Statements in Python (if/elif/else) 80
4.2 Bucles
Cuando queremos hacer algo más de una vez, necesitamos recurrir a un bucle. En esta
sección veremos las distintas sentencias en Python que nos permiten repetir un bloque de
código.1
Como hemos visto en este ejemplo, break nos permite finalizar el bucle una vez que
hemos llegado al máximo número de preguntas. Pero si no hubiéramos llegado a dicho
límite, el bucle habría seguido hasta que el usuario indicara que quiere salir.
Otra forma de resolver este ejercicio sería incorporar una condición al bucle:
while want_exit == N and num_questions < 4:
...
Comprobar la rotura
Nivel intermedio
Python nos ofrece la posibilidad de detectar si el bucle ha acabado de forma ordinaria,
esto es, ha finalizado por no cumplirse la condición establecida. Para ello podemos hacer
uso de la sentencia else como parte del propio bucle. Si el bucle while finaliza
normalmente (sin llamada a break) el flujo de control pasa a la sentencia opcional else.
Veamos su comportamiento siguiendo con el ejemplo que venimos trabajando:
>>> want_exit = N
>>> num_questions = 0
>>> while want_exit == N:
... print(Hola qué tal)
... want_exit = input(¿Quiere salir? [S/N] )
... num_questions += 1
... if num_questions == 4:
... print(Máximo número de preguntas alcanzado)
... break
... else:
... print(Usted ha decidido salir)
... print(Ciao)
Hola qué tal
Resumen libro de Sergio Delgado Quintero -82-
¿Quiere salir? [S/N] S
Usted ha decidido salir
Ciao
Importante: Si hubiéramos agotado el número de preguntas NO se habría ejecutado la
cláusula else del bucle ya que habríamos roto el flujo con un break.
Ejecución paso a paso a través de Python Tutor:
https://cutt.ly/xNho3di
124 Capítulo 4. Control de flujo
Aprende Python
Continuar un bucle
Nivel intermedio
Hay situaciones en las que, en vez de romper un bucle, nos interesa saltar adelante
hacia 82
la siguiente repetición. Para ello Python nos ofrece la sentencia continue que hace
precisamente eso, descartar el resto del código del bucle y saltar a la siguiente iteración.
Continuamos con el ejemplo anterior y vamos a contar el número de respuestas válidas:
>>> want_exit = N
>>> valid_options = 0
>>> while want_exit == N:
... print(Hola qué tal)
... want_exit = input(¿Quiere salir? [S/N] )
... if want_exit not in SN:
... want_exit = N
... continue
... valid_options += 1
... print(f{valid_options} respuestas válidas)
... print(Ciao!)
Hola qué tal
¿Quiere salir? [S/N] N
Hola qué tal
¿Quiere salir? [S/N] X
Hola qué tal
¿Quiere salir? [S/N] Z
Hola qué tal
¿Quiere salir? [S/N] S
2 respuestas válidas
Ciao!
Ejecución paso a paso a través de Python Tutor:
https://cutt.ly/BNhkOhP
Bucle infinito
Si no establecemos correctamente la condición de parada o bien el valor de alguna
variable
está fuera de control, es posible que lleguemos a una situación de bucle infinito, del que
nunca
podamos salir. Veamos un ejemplo de esto:
>>> num = 1
>>> while num != 10:
... num += 2
(continué en la próxima página)
4.2. Bucles 125
Aprende Python
(proviene de la página anterior)
Resumen libro de Sergio Delgado Quintero -83-
...
# CTRL-C
KeyboardInterrupt
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
El problema que surje es que la variable num toma los valores 1, 3, 5, 7, 9, 11, ..
. por lo que nunca se cumple la condición de parada del bucle. Esto hace que repitamos
«eternamente» la instrucción de incremento.
Ejecución paso a paso a través de Python Tutor:
https://cutt.ly/AfrZroa
Una posible solución a este error es reescribir la condición de parada en el bucle:
>>> num = 1
>>> while num < 10: 83
... num += 2
...
Truco: Para abortar una situación de bucle infinito podemos pulsar en el teclado
la combinación CTRL-C. Se puede ver reflejado en el intérprete de Python por
KeyboardInterrupt.
Hay veces que un supuesto bucle «infinito» puede ayudarnos a resolver un problema.
Imaginemos que queremos escribir un programa que ayude al profesorado a introducir las
notas de un examen. Si la nota no está en el intervalo [0, 10] mostramos un mensaje de
error,
en otro caso seguimos pidiendo valores:
>>> while True:
... mark = float(input(Introduzca nueva nota: ))
... if not(0 <= mark <= 10):
... print(Nota fuera de rango)
... break
... print(mark)
...
Introduzca nueva nota: 5
5.0
Introduzca nueva nota: 3
3.0
Introduzca nueva nota: 11
Nota fuera de rango
126 Capítulo 4. Control de flujo
Aprende Python
Ejercicio
Escriba un programa que encuentre todos los múltiplos de 5 menores que un valor dado:
Ejemplo
• Entrada: 36
• Salida: 5 10 15 20 25 30 35
4.2.2 La sentencia for
Python permite recorrer aquellos tipos de datos que sean iterables, es decir, que admitan
iterar2 sobre ellos. Algunos ejemplos de tipos y estructuras de datos que permiten ser
iteradas
(recorridas) son: cadenas de texto, listas, diccionarios, ficheros, etc. La sentencia for nos
permite realizar esta acción.
A continuación se plantea un ejemplo en el que vamos a recorrer (iterar) una cadena de
texto:
>>> word = Python
Resumen libro de Sergio Delgado Quintero -84-
>>> for letter in word:
... print(letter)
...
P
y
t
h
o
n
La clave aquí está en darse cuenta que el bucle va tomando, en cada iteración, cada uno
de
los elementos de la variable que especifiquemos. En este caso concreto letter va tomando
cada una de las letras que existen en word, porque una cadena de texto está formada por 84
elementos que son caracteres.
Ejecución paso a paso a través de Python Tutor:
https://cutt.ly/Pft6R2e
Importante: La variable que utilizamos en el bucle for para ir tomando los valores puede
tener cualquier nombre. Al fin y al cabo es una variable que definimos según nuestras
2 Realizar cierta acción varias veces. En este caso la acción es tomar cada elemento.
4.2. Bucles 127
Aprende Python
necesidades. Tener en cuenta que se suele usar un nombre en singular.
Romper un bucle for
Una sentencia break dentro de un for rompe el bucle, igual que veíamos para los bucles
while. Veamos un ejemplo con el código anterior. En este caso vamos a recorrer una
cadena
de texto y pararemos el bucle cuando encontremos una letra t minúscula:
>>> word = Python
>>> for letter in word:
... if letter == t:
... break
... print(letter)
...
P
y
Ejecución paso a paso a través de Python Tutor:
https://cutt.ly/zfyqkbJ
Truco: Tanto la comprobación de rotura de un bucle como la continuación a la siguiente
iteración se llevan a cabo del mismo modo que hemos visto con los bucles de tipo while.
Ejercicio
Dada una cadena de texto, indique el número de vocales que tiene.
Ejemplo
• Entrada: Supercalifragilisticoespialidoso
• Salida: 15
Secuencias de números
Es muy habitual hacer uso de secuencias de números en bucles. Python no tiene una
instrucción específica para ello. Lo que sí aporta es una función range() que devuelve un
flujo de números en el rango especificado. Una de las grandes ventajas es que la «lista»
generada no se construye explícitamente, sino que cada valor se genera bajo demanda.
Esta técnica mejora el consumo de recursos, especialmente en términos de memoria.
Resumen libro de Sergio Delgado Quintero -85-
La técnica para la generación de secuencias de números es muy similar a la utilizada en
los «slices» de cadenas de texto. En este caso disponemos de la función range(start,
stop, step):
• start: Es opcional y tiene valor por defecto 0.
• stop: es obligatorio (siempre se llega a 1 menos que este valor).
• step: es opcional y tiene valor por defecto 1.
range() devuelve un objeto iterable, así que iremos obteniendo los valores paso a paso
con
una sentencia for ... in3. Veamos diferentes ejemplos de uso:
Rango: [0, 1, 2]
>>> for i in range(0, 3):
... print(i)
... 85
0
1
2
>>> for i in range(3): # No hace falta indicar el inicio si es 0
... print(i)
...
0
1
2
Rango: [1, 3, 5]
>>> for i in range(1, 6, 2):
... print(i)
...
1
3
5
Rango: [2, 1, 0]
3 O convertir el objeto a una secuencia como una lista.
4.2. Bucles 129
Aprende Python
>>> for i in range(2, -1, -1):
... print(i)
...
2
1
0
Ejecución paso a paso a través de Python Tutor:
https://cutt.ly/vfywE45
Truco: Se suelen utilizar nombres de variables i, j, k para lo que se denominan
contadores.
Este tipo de variables toman valores numéricos enteros como en los ejemplos anteriores.
No
conviene generalizar el uso de estas variables a situaciones en las que, claramente,
tenemos la
posibilidad de asignar un nombre semánticamente más significativo. Esto viene de
tiempos
antiguos en FORTRAN donde i era la primera letra que tenía valor entero por defecto.
Ejercicio
Determine si un número dado es un número primo.
Resumen libro de Sergio Delgado Quintero -86-
No es necesario implementar ningún algoritmo en concreto. La idea es probar los
números menores al dado e ir viendo si las divisiones tienen resto cero o no.
¿Podría optimizar su código? ¿Realmente es necesario probar con tantos divisores?
Ejemplo
• Entrada: 11
• Salida: Es primo
Usando el guión bajo
Nivel avanzado
Hay situaciones en las que no necesitamos usar la variable que toma valores en el
rango, sino que únicamente queremos repetir una acción un número determinado de
veces.
Para estos casos se suele recomendar usar el guión bajo _ como nombre de variable,
que da a entender que no estamos usando esta variable de forma explícita: 86
>>> for _ in range(10):
... print(Repeat me 10 times!)
(continué en la próxima página)
130 Capítulo 4. Control de flujo
Aprende Python
(proviene de la página anterior)
...
Repeat me 10 times!
Repeat me 10 times!
Repeat me 10 times!
Repeat me 10 times!
Repeat me 10 times!
Repeat me 10 times!
Repeat me 10 times!
Repeat me 10 times!
Repeat me 10 times!
Repeat me 10 times!
Ejercicio
Imprima los 100 primeros números de la sucesión de Fibonacci:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, . . .
4.2.3 Bucles anidados
Como ya vimos en las sentencias condicionales, el anidamiento es una técnica por la que
incluimos distintos niveles de encapsulamiento de sentencias, unas dentro de otras, con
mayor nivel de profundidad. En el caso de los bucles también es posible hacer
anidamiento.
Veamos un ejemplo de 2 bucles anidados en el que generamos todas las tablas de
multiplicar:
>>> for i in range(1, 10):
... for j in range(1, 10):
... result = i * j
... print(f{i} * {j} = {result})
...
1x1=1
1x2=2
1x3=3
1x4=4
1x5=5
Resumen libro de Sergio Delgado Quintero -87-
1x6=6
1x7=7
1x8=8
1x9=9
2x1=2
2x2=4
2x3=6
(continué en la próxima página)
4.2. Bucles 131
Aprende Python
(proviene de la página anterior)
2x4=8
2 x 5 = 10 87
2 x 6 = 12
2 x 7 = 14
2 x 8 = 16
2 x 9 = 18
3x1=3
3x2=6
3x3=9
3 x 4 = 12
3 x 5 = 15
3 x 6 = 18
3 x 7 = 21
3 x 8 = 24
3 x 9 = 27
4x1=4
4x2=8
4 x 3 = 12
4 x 4 = 16
4 x 5 = 20
4 x 6 = 24
4 x 7 = 28
4 x 8 = 32
4 x 9 = 36
5x1=5
5 x 2 = 10
5 x 3 = 15
5 x 4 = 20
5 x 5 = 25
5 x 6 = 30
5 x 7 = 35
5 x 8 = 40
5 x 9 = 45
6x1=6
6 x 2 = 12
6 x 3 = 18
6 x 4 = 24
6 x 5 = 30
6 x 6 = 36
6 x 7 = 42
6 x 8 = 48
Resumen libro de Sergio Delgado Quintero -88-
6 x 9 = 54
7x1=7
7 x 2 = 14
7 x 3 = 21
(continué en la próxima página)
132 Capítulo 4. Control de flujo
Aprende Python
(proviene de la página anterior)
7 x 4 = 28
7 x 5 = 35
7 x 6 = 42
7 x 7 = 49
7 x 8 = 56 88
7 x 9 = 63
8x1=8
8 x 2 = 16
8 x 3 = 24
8 x 4 = 32
8 x 5 = 40
8 x 6 = 48
8 x 7 = 56
8 x 8 = 64
8 x 9 = 72
9x1=9
9 x 2 = 18
9 x 3 = 27
9 x 4 = 36
9 x 5 = 45
9 x 6 = 54
9 x 7 = 63
9 x 8 = 72
9 x 9 = 81
Lo que está ocurriendo en este código es que, para cada valor que toma la variable i, la
otra variable j toma todos sus valores. Como resultado tenemos una combinación
completa de los valores en el rango especificado.
Ejecución paso a paso a través de Python Tutor:
https://cutt.ly/vfyeWvj
Nota:
• Podemos añadir todos los niveles de anidamiento que queramos. Eso sí, hay que tener
en cuenta que cada nuevo nivel de anidamiento supone un importante aumento de la
complejidad ciclomática de nuestro código, lo que se traduce en mayores tiempos de
ejecución.
• Los bucles anidados también se pueden aplicar en la sentencia while.
Ejercicio
Dado su tamaño, muestre por pantalla un mosaico donde la diagonal principal esté
4.2. Bucles 133
Aprende Python
representada por X, la parte inferior por D y la parte superior por U.
Ejemplo
• Entrada: 5
• Salida:
XUUUU
Resumen libro de Sergio Delgado Quintero -89-
DXUUU
DDXUU
DDDXU
DDDDX
EJERCICIOS DE REPASO
1. Escriba un programa que encuentre la mínima secuencia de múltiplos de 3
(distintos) cuya • Entrada: 45
• Salida: 0, 3, 6, 9, 12, 15
2. Escriba un programa que pida nombre y apellidos de una persona (usando un solo
input) y repita la pregunta mientras el nombre no esté en formato título (solución).
¿Su nombre? ana torres blanco
Error. Debe escribirlo correctamente 89
¿Su nombre? Ana torres blanco
Error. Debe escribirlo correctamente
¿Su nombre? Ana Torres blanco
Error. Debe escribirlo correctamente
¿Su nombre? Ana Torres Blanco
3. Escriba un programa en Python que realice las siguientes 9 multiplicaciones. ¿Nota
algo raro en el resultado? (solución)
1・1
11 ・ 11
111 ・ 111
...
111111111 ・ 111111111
4. Escriba un programa en Python que acepte una cadena de texto e indique si
todos sus 134 Capítulo 4. Control de flujo
Aprende Python
• Entrada: hello-world
• Salida: Se han encontrado caracteres no alfabéticos
5. Escriba un programa en Python que acepte un número entero 𝑛 y realice el siguiente
cálculo de productos sucesivos (solución):
Π︁𝑛
𝑖=1
𝑖2 = 12 ・ 22 ・ 32 ・ ・ ・ ・ ・ 𝑛2
6. Escriba un programa en Python que acepte dos cadenas de texto y compute el
producto • Entrada: str1=abc; str2=123
• Salida: a1 a2 a3 b1 b2 b3 c1 c2 c3
7. Escriba un programa en Python que acepte dos valores enteros (𝑥 e 𝑦) que
representarán • Entrada: objetivo_x=7; objetivo_y=8;
• Salida: (0, 0) (1, 2) (3, 3) (4, 5) (6, 6) (7, 8)
8. Escriba un programa que calcule la distancia hamming entre dos cadenas de
texto de la • Entrada: 0001010011101 y 0000110010001
• Salida: 4
9. Escriba un programa que calcule el máximo común divisor entre dos números
enteros. • Entrada: a=12; b=44
• Salida: 4
10. Escriba un programa que muestre por pantalla todas las fichas del dominó. La ficha
«en blanco» se puede representar con un 0 (solución).
4.2. Bucles 135
Aprende Python
0|0 0|1 0|2 0|3 0|4 0|5 0|6
Resumen libro de Sergio Delgado Quintero -90-
1|1 1|2 1|3 1|4 1|5 1|6
2|2 2|3 2|4 2|5 2|6
3|3 3|4 3|5 3|6
4|4 4|5 4|6
5|5 5|6
6|6
11. Escriba un programa que calcule el valor de 𝑥 para el que la función 𝑓(𝑥) = 𝑥2−6𝑥+3
obtiene su menor resultado. Centre la búsqueda en el rango [−9, 9] sólo con valores
enteros (solución).
El resultado es: 𝑥 = 3 y 𝑓(𝑥) = −6
12. Escriba un programa que muestre (por filas) la Tabla ASCII, empezando con el código
33 y terminando con el 127 (solución):
136 Capítulo 4. Control de flujo 90
Aprende Python
033 ! 034 " 035 # 036 $ 037 %
038 & 039 040 ( 041 ) 042 *
043 + 044 , 045 - 046 . 047 /
048 0 049 1 050 2 051 3 052 4
053 5 054 6 055 7 056 8 057 9
058 : 059 ; 060 < 061 = 062 >
063 ? 064 @ 065 A 066 B 067 C
068 D 069 E 070 F 071 G 072 H
073 I 074 J 075 K 076 L 077 M
078 N 079 O 080 P 081 Q 082 R
083 S 084 T 085 U 086 V 087 W
088 X 089 Y 090 Z 091 [ 092 \
093 ] 094 ^ 095 _ 096 097 a
098 b 099 c 100 d 101 e 102 f
103 g 104 h 105 i 106 j 107 k
108 l 109 m 110 n 111 o 112 p
113 q 114 r 115 s 116 t 117 u
118 v 119 w 120 x 121 y 122 z
123 { 124 | 125 } 126 ~ 127
13. Escriba un programa que permita al usuario adivinar un número. Indicar si el número
buscado es menor o mayor que el que se está preguntando y mostrar igualmente el
número de intentos hasta encontrar el número objetivo (solución):
Introduzca número: 50
Mayor
Introduzca número: 100
Menor
Introduzca número: 90
Menor
Introduzca número: 87
¡Enhorabuena! Has encontrado el número en 4 intentos
14. pycheck: tennis_game
15. pycheck: tennis_set
EJERCICIOS EXTERNOS
1. Summation
2. Find nearest square number
3. Bin to decimal
4. altERnaTIng cAsE
5. Fake binary
Resumen libro de Sergio Delgado Quintero -91-
4.2. Bucles 137
Aprende Python
6. Correct the mistakes of the character recognition software
7. String cleaning
8. Sum of multiples
9. ASCII Total
10. Collatz Conjecture (3n+1)
AMPLIAR CONOCIMIENTOS
• The Python range() Function
• How to Write Pythonic Loops
• For Loops in Python (Definite Iteration)
• Python «while» Loops (Indefinite Iteration)
91
CAPÍTULO 5
Estructuras de datos
Si bien ya hemos visto una sección sobre Tipos de datos, podríamos hablar de tipos de
datos más complejos en Python que se constituyen en estructuras de datos. Si
pensamos en estos elementos como átomos, las estructuras de datos que vamos a ver
sería moléculas. Es decir, combinamos los tipos básicos de formas más complejas. De
hecho, esta distinción se hace en el Tutorial oficial de Python. Trataremos distintas
estructuras de datos como listas, tuplas, diccionarios y conjuntos.
5.1 Listas
Las listas permiten almacenar objetos mediante un orden definido y con posibilidad
de duplicados. Las listas son estructuras de datos mutables, lo que significa que
podemos añadir, eliminar o modificar sus elementos.1
5.1.1 Creando listas
Una lista está compuesta por cero o más elementos. En Python debemos escribir estos
elementos separados por comas y dentro de corchetes. Veamos algunos ejemplos de
listas:
>>> empty_list = []
>>> languages = [Python, Ruby, Javascript]
>>> fibonacci = [0, 1, 1, 2, 3, 5, 8, 13]
Resumen libro de Sergio Delgado Quintero -92-
>>> data = [Tenerife, {cielo: limpio, temp: 24}, 3718, (28.2933947, -16.
˓→5226597)]
Nota: Una lista puede contener tipos de datos heterogéneos, lo que la hace una
estructura
1 Foto original de portada por Mike Arney en Unsplash.
140 Capítulo 5. Estructuras de datos
Aprende Python
de datos muy versátil.
Ejecución paso a paso a través de Python Tutor:
https://cutt.ly/Ofiiare
Advertencia: Aunque está permitido, NUNCA llames list a una variable porque
destruirías la función que nos permite crear listas. Y tampoco uses nombres derivados
como _list o list_ ya que no son nombres representativos que identifiquen el propósito 92
de la variable.
Ejercicio
Cree una lista con las 5 ciudades que más le gusten.
5.1.2 Conversión
Para convertir otros tipos de datos en una lista podemos usar la función list():
>>> # conversión desde una cadena de texto
>>> list(Python)
[P, y, t, h, o, n]
Si nos fijamos en lo que ha pasado, al convertir la cadena de texto Python se ha creado
una lista con 6 elementos, donde cada uno de ellos representa un carácter de la cadena.
Podemos extender este comportamiento a cualquier otro tipo de datos que permita ser
iterado (iterables).
Otro ejemplo interesante de conversión puede ser la de los rangos. En este caso
queremos obtener una lista explícita con los valores que constituyen el rango [0, 9]:
>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
5.1. Listas 141
Aprende Python
Lista vacía
Existe una manera particular de usar list() y es no pasarle ningún argumento. En este
caso estaremos queriendo convertir el «vacío» en una lista, con lo que obtendremos una
lista
vacía:
>>> list()
[]
Truco: Para crear una lista vacía, se suele recomendar el uso de [] frente a list(), no
sólo por ser más pitónico sino por tener (en promedio) un mejor rendimiento en tiempos
de
ejecución.
5.1.3 Operaciones con listas
Obtener un elemento
Igual que en el caso de las cadenas de texto, podemos obtener un elemento de una lista a
través del índice (lugar) que ocupa. Veamos un ejemplo:
>>> shopping = [Agua, Huevos, Aceite]
>>> shopping[0]
Agua
>>> shopping[1]
Huevos
>>> shopping[2]
Resumen libro de Sergio Delgado Quintero -93-
Aceite
>>> shopping[-1] # acceso con índice negativo
Aceite
El índice que usemos para acceder a los elementos de una lista tiene que estar
comprendido
entre los límites de la misma. Si usamos un índice antes del comienzo o después del final
obtendremos un error (excepción):
>>> shopping = [Agua, Huevos, Aceite]
>>> shopping[3]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
(continué en la próxima página)
142 Capítulo 5. Estructuras de datos 93
Aprende Python
(proviene de la página anterior)
IndexError: list index out of range
>>> shopping[-5]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
Trocear una lista
El troceado de listas funciona de manera totalmente análoga al troceado de cadenas.
Veamos
algunos ejemplos:
>>> shopping = [Agua, Huevos, Aceite, Sal, Limón]
>>> shopping[0:3]
[Agua, Huevos, Aceite]
>>> shopping[:3]
[Agua, Huevos, Aceite]
>>> shopping[2:4]
[Aceite, Sal]
>>> shopping[-1:-4:-1]
[Limón, Sal, Aceite]
>>> # Equivale a invertir la lista
>>> shopping[::-1]
[Limón, Sal, Aceite, Huevos, Agua]
En el troceado de listas, a diferencia de lo que ocurre al obtener elementos, no debemos
preocuparnos por acceder a índices inválidos (fuera de rango) ya que Python los
restringirá
a los límites de la lista:
>>> shopping
[Agua, Huevos, Aceite, Sal, Limón]
>>> shopping[10:]
[]
>>> shopping[-100:2]
[Agua, Huevos]
(continué en la próxima página)
5.1. Listas 143
Aprende Python
(proviene de la página anterior)
>>> shopping[2:100]
[Aceite, Sal, Limón]
Resumen libro de Sergio Delgado Quintero -94-
Importante: Ninguna de las operaciones anteriores modifican la lista original,
simplemente
devuelven una lista nueva.
Invertir una lista
Python nos ofrece, al menos, tres mecanismos para invertir los elementos de una lista:
Conservando la lista original: Opción 1: Mediante troceado de listas con step negativo:
>>> shopping
[Agua, Huevos, Aceite, Sal, Limón]
>>> shopping[::-1]
[Limón, Sal, Aceite, Huevos, Agua]
Opción 2: Mediante la función reversed():
>>> shopping
[Agua, Huevos, Aceite, Sal, Limón] 94
>>> list(reversed(shopping))
[Limón, Sal, Aceite, Huevos, Agua]
Modificando la lista original: Utilizando la función reverse() (nótese que es sin «d» al
final):
>>> shopping
[Agua, Huevos, Aceite, Sal, Limón]
>>> shopping.reverse()
>>> shopping
[Limón, Sal, Aceite, Huevos, Agua]
144 Capítulo 5. Estructuras de datos
Aprende Python
Añadir al final de la lista
Una de las operaciones más utilizadas en listas es añadir elementos al final de las
mismas.
Para ello Python nos ofrece la función append(). Se trata de un método destructivo que
modifica la lista original:
>>> shopping = [Agua, Huevos, Aceite]
>>> shopping.append(Atún)
>>> shopping
[Agua, Huevos, Aceite, Atún]
Creando desde vacío
Una forma muy habitual de trabajar con listas es empezar con una vacía e ir añadiendo
elementos poco a poco. Se podría hablar de un patrón creación.
Supongamos un ejemplo en el que queremos construir una lista con los números pares
del
[0, 20):
>>> even_numbers = []
>>> for i in range(20):
... if i % 2 == 0:
... even_numbers.append(i)
...
>>> even_numbers
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
Ejecución paso a paso a través de Python Tutor:
https://cutt.ly/2fiS9Ax
Añadir en cualquier posición de una lista
Ya hemos visto cómo añadir elementos al final de una lista. Sin embargo, Python ofrece
una función insert() que vendría a ser una generalización de la anterior, para incorporar
Resumen libro de Sergio Delgado Quintero -95-
elementos en cualquier posición. Simplemente debemos especificar el índice de inserción
y el
elemento en cuestión. También se trata de una función destructiva2:
2 Cuando hablamos de que una función/método es «destructiva/o» significa que modifica
la lista (objeto)
original, no que la destruye.
5.1. Listas 145
Aprende Python
>>> shopping = [Agua, Huevos, Aceite]
>>> shopping.insert(1, Jamón)
>>> shopping
[Agua, Jamón, Huevos, Aceite]
>>> shopping.insert(3, Queso) 95
>>> shopping
[Agua, Jamón, Huevos, Queso, Aceite]
Nota: El índice que especificamos en la función insert() lo podemos intepretar como la
posición delante (a la izquierda) de la cual vamos a colocar el nuevo valor en la lista.
Al igual que ocurría con el troceado de listas, en este tipo de inserciones no obtendremos
un
error si especificamos índices fuera de los límites de la lista. Estos se ajustarán al
principio
o al final en función del valor que indiquemos:
>>> shopping = [Agua, Huevos, Aceite]
>>> shopping.insert(100, Mermelada)
>>> shopping
[Agua, Huevos, Aceite, Mermelada]
>>> shopping.insert(-100, Arroz)
>>> shopping
[Arroz, Agua, Huevos, Aceite, Mermelada]
Consejo: Aunque es posible utilizar insert() para añadir elementos al final de una
lista, siempre se recomienda usar append() por su mayor legibilidad:
>>> values = [1, 2, 3]
>>> values.append(4)
>>> values
[1, 2, 3, 4]
>>> values = [1, 2, 3]
>>> values.insert(len(values), 4) # dont do it!
>>> values
[1, 2, 3, 4]
146 Capítulo 5. Estructuras de datos
Aprende Python
Repetir elementos
Al igual que con las cadenas de texto, el operador * nos permite repetir los elementos de
una lista:
>>> shopping = [Agua, Huevos, Aceite]
>>> shopping * 3
[Agua,
Huevos,
Aceite,
Agua,
Huevos,
Aceite,
Resumen libro de Sergio Delgado Quintero -96-
Agua,
Huevos,
Aceite]
Combinar listas
Python nos ofrece dos aproximaciones para combinar listas:
Conservando la lista original: Mediante el operador + o +=:
>>> shopping = [Agua, Huevos, Aceite]
>>> fruitshop = [Naranja, Manzana, Piña]
>>> shopping + fruitshop
[Agua, Huevos, Aceite, Naranja, Manzana, Piña]
Modificando la lista original: Mediante la función extend():
>>> shopping = [Agua, Huevos, Aceite]
>>> fruitshop = [Naranja, Manzana, Piña] 96
>>> shopping.extend(fruitshop)
>>> shopping
[Agua, Huevos, Aceite, Naranja, Manzana, Piña]
Hay que tener en cuenta que extend() funciona adecuadamente si pasamos una lista
como
argumento. En otro caso, quizás los resultados no sean los esperados. Veamos un
ejemplo:
>>> shopping = [Agua, Huevos, Aceite]
>>> shopping.extend(Limón)
(continué en la próxima página)
5.1. Listas 147
Aprende Python
(proviene de la página anterior)
>>> shopping
[Agua, Huevos, Aceite, L, i, m, ó, n]
El motivo es que extend() «recorre» (o itera) sobre cada uno de los elementos del objeto
en cuestión. En el caso anterior, al ser una cadena de texto, está formada por caracteres.
De
ahí el resultado que obtenemos.
Se podría pensar en el uso de append() para combinar listas. La realidad es que no
funciona
exactamente como esperamos; la segunda lista se añadiría como una sublista de la
principal:
>>> shopping = [Agua, Huevos, Aceite]
>>> fruitshop = [Naranja, Manzana, Piña]
>>> shopping.append(fruitshop)
>>> shopping
[Agua, Huevos, Aceite, [Naranja, Manzana, Piña]]
Modificar una lista
Del mismo modo que se accede a un elemento utilizando su índice, también podemos
modificarlo:
>>> shopping = [Agua, Huevos, Aceite]
>>> shopping[0]
Agua
>>> shopping[0] = Jugo
>>> shopping
[Jugo, Huevos, Aceite]
En el caso de acceder a un índice no válido de la lista, incluso para modificar,
obtendremos
Resumen libro de Sergio Delgado Quintero -97-
un error:
>>> shopping[100] = Chocolate
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list assignment index out of range
148 Capítulo 5. Estructuras de datos
Aprende Python
Modificar con troceado
No sólo es posible modificar un elemento de cada vez, sino que podemos asignar valores
a
trozos de una lista:
>>> shopping = [Agua, Huevos, Aceite, Sal, Limón]
>>> shopping[1:4] 97
[Huevos, Aceite, Sal]
>>> shopping[1:4] = [Atún, Pasta]
>>> shopping
[Agua, Atún, Pasta, Limón]
Nota: La lista que asignamos no necesariamente debe tener la misma longitud que el
trozo
que sustituimos.
Borrar elementos
Python nos ofrece, al menos, cuatro formas para borrar elementos en una lista:
Por su índice: Mediante la sentencia del:
>>> shopping = [Agua, Huevos, Aceite, Sal, Limón]
>>> del shopping[3]
>>> shopping
[Agua, Huevos, Aceite, Limón]
Por su valor: Mediante la función remove():
>>> shopping = [Agua, Huevos, Aceite, Sal, Limón]
>>> shopping.remove(Sal)
>>> shopping
[Agua, Huevos, Aceite, Limón]
Advertencia: Si existen valores duplicados, la función remove() sólo borrará la
primera ocurrencia.
5.1. Listas 149
Aprende Python
Por su índice (con extracción): La sentencia del y la función remove() efectivamente
borran el elemento indicado de la lista, pero no «devuelven»3 nada. Sin embargo,
Python nos ofrece la función pop() que además de borrar, nos «recupera» el elemento;
algo así como una extracción. Lo podemos ver como una combinación de acceso +
borrado:
>>> shopping = [Agua, Huevos, Aceite, Sal, Limón]
>>> shopping.pop()
Limón
>>> shopping
[Agua, Huevos, Aceite, Sal]
>>> shopping.pop(2)
Aceite
>>> shopping
[Agua, Huevos, Sal]
Nota: Si usamos la función pop() sin pasarle ningún argumento, por defecto usará
el índice -1, es decir, el último elemento de la lista. Pero también podemos indicarle el
Resumen libro de Sergio Delgado Quintero -98-
índice del elemento a extraer.
Por su rango: Mediante troceado de listas:
>>> shopping = [Agua, Huevos, Aceite, Sal, Limón]
>>> shopping[1:4] = []
>>> shopping
[Agua, Limón]
Borrado completo de la lista
Python nos ofrece, al menos, dos formas para borrar una lista por completo:
1. Utilizando la función clear():
>>> shopping = [Agua, Huevos, Aceite, Sal, Limón]
(continué en la próxima página)
3 Más adelante veremos el comportamiento de las funciones. Devolver o retornar un valor
es el resultado 98
de aplicar una función.
150 Capítulo 5. Estructuras de datos
Aprende Python
(proviene de la página anterior)
>>> shopping.clear() # Borrado in-situ
>>> shopping
[]
2. «Reinicializando» la lista a vacío con []:
>>> shopping = [Agua, Huevos, Aceite, Sal, Limón]
>>> shopping = [] # Nueva zona de memoria
>>> shopping
[]
Nivel avanzado
La diferencia entre ambos métodos tiene que ver con cuestiones internas de gestión de
memoria y de rendimiento:
>>> shopping = [Agua, Huevos, Aceite, Sal, Limón]
>>> id(shopping)
4416018560
>>> shopping.clear()
>>> id(shopping) # se mantiene la misma "posición de memoria"
4416018560
>>> shopping = [Agua, Huevos, ______________Aceite, Sal, Limón]
>>> id(shopping)
4458688576
>>> shopping = []
>>> id(shopping) # se crea una nueva "posición de memoria"
4458851520
Encontrar un elemento
Si queremos descubrir el índice que corresponde a un determinado valor dentro la lista
podemos usar la función index() para ello:
>>> shopping = [Agua, Huevos, Aceite, Sal, Limón]
>>> shopping.index(Huevos)
1
Tener en cuenta que si el elemento que buscamos no está en la lista, obtendremos un
error:
5.1. Listas 151
Aprende Python
>>> shopping = [Agua, Huevos, Aceite, Sal, Limón]
>>> shopping.index(Pollo)
Resumen libro de Sergio Delgado Quintero -99-
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: Pollo is not in list
Nota: Si buscamos un valor que existe más de una vez en una lista, la función index()
sólo
nos devolverá el índice de la primera ocurrencia.
Pertenencia de un elemento
Si queremos comprobar la existencia de un determinado elemento en una lista, podríamos
buscar su índice, pero la forma pitónica de hacerlo es utilizar el operador in:
>>> shopping = [Agua, Huevos, Aceite, Sal, Limón]
>>> Aceite in shopping
True
>>> Pollo in shopping 99
False
Nota: El operador in siempre devuelve un valor booleano, es decir, verdadero o falso.
Ejercicio
Determine si una cadena de texto dada es un isograma, es decir, no se repite ninguna
letra.
Ejemplos válidos de isogramas:
• lumberjacks
• background
• downstream
• six-year-old
152 Capítulo 5. Estructuras de datos
Aprende Python
Número de ocurrencias
Para contar cuántas veces aparece un determinado valor dentro de una lista podemos
usar
la función count():
>>> sheldon_greeting = [Penny, Penny, Penny]
>>> sheldon_greeting.count(Howard)
0
>>> sheldon_greeting.count(Penny)
3
Convertir lista a cadena de texto
Dada una lista, podemos convetirla a una cadena de texto, uniendo todos sus elementos
mediante algún separador. Para ello hacemos uso de la función join() con la siguiente
estructura:
108
5.2 Tuplas
El concepto de tupla es muy similar al de lista. Aunque hay algunas diferencias menores,
lo fundamental es que, mientras una lista es mutable y se puede modificar, una tupla no
admite cambios y por lo tanto, es inmutable.1
Tuplas de un elemento
Hay que prestar especial atención cuando vamos a crear una tupla de un único
elemento.
La intención primera sería hacerlo de la siguiente manera:
>>> one_item_tuple = (Papá Noel)
>>> one_item_tuple
Papá Noel 111
>>> type(one_item_tuple)
str
Realmente, hemos creado una variable de tipo str (cadena de texto). Para crear una tupla
de un elemento debemos añadir una coma al final:
>>> one_item_tuple = (Papá Noel,)
>>> one_item_tuple
(Papá Noel,)
>>> type(one_item_tuple)
tuple
Tuplas sin paréntesis
Según el caso, hay veces que nos podemos encontrar con tuplas que no llevan
paréntesis.
Quizás no está tan extendido, pero a efectos prácticos tiene el mismo resultado. Veamos
algunos ejemplos de ello:
>>> one_item_tuple = Papá Noel,
three_wise_men = ("Melchor", "Gaspar", "Baltasar")
print(three_wise_men)
Advertencia: Aunque está permitido, NUNCA llames tuple a una variable porque
destruirías la función que nos permite crear tuplas. Y tampoco uses nombres derivados
como _tuple o tuple_ ya que no son nombres representativos que identifiquen el propósito
de la variable.
21
Intercambio de valores
A través del desempaquetado de tuplas podemos llevar a cabo el intercambio de los
valores de dos variables de manera directa:
a = 10
b = 50
print(f"antes a={a} b ={b} ")
a, b = b,a
print(f"despues a={a} b ={b} ")
Nota: A priori puede parecer que esto es algo «natural», pero en la gran mayoría de
lenguajes de programación no es posible hacer este intercambio de forma «directa» ya
que necesitamos recurrir a una tercera variable «auxiliar» como almacén temporal en el
paso intermedio de traspaso de valores.
Desempaquetado extendido
No tenemos que ceñirnos a realizar desempaquetado uno a uno. También podemos
extenderlo e indicar ciertos «grupos» de elementos mediante el operador *.
Veamos un ejemplo:
Desempaquetado genérico
El desempaquetado de tuplas es extensible a cualquier tipo de datos que sea iterable.
Veamos
algunos ejemplos de ello.
114
Sobre cadenas de texto:
>>> oxygen = O2
>>> first, last = oxygen
>>> first, last
(O, 2)
>>> text = Hello, World!
>>> head, *body, tail = text
>>> head, body, tail
(H, [e, l, l, o, ,, , W, o, r, l, d], !)
Sobre listas:
>>> writer1, writer2, writer3 = [Virginia Woolf, Jane Austen, Mary Shelley]
>>> writer1, writer2, writer3
(Virginia Woolf, Jane Austen, Mary Shelley)
>>> text = Hello, World!
>>> word1, word2 = text.split()
>>> word1, word2
(Hello,, World!)
5.3 Diccionarios
Resumen libro de Sergio Delgado Quintero -115-
115
116
empty_dict = {}
rae = {
"bifronte": "De dos frentes o dos caras",
"anarcoide": "Que tiende al desorden",
"montuvio": "Campesino de la costa"
}
population_can = {
2015: 2_135_209,
2016: 2_154_924,
2017: 2_177_048,
2018: 2_206_901,
2019: 2_220_270
}
print(empty_dict)
print(rae)
print(population_can)
Resumen libro de Sergio Delgado Quintero -117-
{}
{'bifronte': 'De dos frentes o dos caras', 'anarcoide': 'Que tiende al
desorden', 'montuvio': 'Campesino de la costa'}
{2015: 2135209, 2016: 2154924, 2017: 2177048, 2018: 2206901, 2019:
2220270}
5.3.2 Conversión
Para convertir otros tipos de datos en un diccionario podemos usar la función dict():
>>> # Diccionario a partir de una lista de cadenas de texto
>>> dict(["a1", "b2"])
{"a": "1", "b": "2"}
>>> # Diccionario a partir de una tupla de cadenas de texto
>>> dict(("a1", "b2"))
{"a": "1", "b": "2"}
>>> # Diccionario a partir de una lista de listas
>>> dict([["a", 1], ["b", 2]])
{"a": 1, "b": 2}
Nota: Si nos fijamos bien, cualquier iterable que tenga una estructura interna de 2
elementos es susceptible de convertirse en un diccionario a través de la función dict().
Diccionario vacío
Existe una manera particular de usar dict() y es no pasarle ningún argumento. En este
caso estaremos queriendo convertir el «vacío» en un diccionario, con lo que obtendremos
un diccionario vacío:
>>> dict()
{}
Truco: Para crear un diccionario vacío, se suele recomendar el uso de {} frente a dict(),
no sólo por ser más pitónico sino por tener (en promedio) un mejor rendimiento en
tiempos de ejecución.
Creación con dict()
También es posible utilizar la función dict() para crear dicionarios y no tener que utilizar
llaves y comillas: Supongamos que queremos transformar la siguiente tabla en un
diccionario:
Resumen libro de Sergio Delgado Quintero -118-
person = dict(
name="Guido",
surname="Van Rossum",
job="Python creator"
) 118
print(person)
Utilizando la construcción mediante dict podemos pasar clave y valor como argumentos
de la función:
>>> person = dict(
... name=Guido,
... surname=Van Rossum,
... job=Python creator
... )
>>> person
{name: Guido, surname: Van Rossum, job: Python creator}
El inconveniente que tiene esta aproximación es que las claves deben ser
identificadores
válidos en Python. Por ejemplo, no se permiten espacios:
>>> person = dict(
... name=Guido van Rossum,
... date of birth=31/01/1956
File "<stdin>", line 3
date of birth=31/01/1956
^
SyntaxError: invalid syntax
Nivel intermedio
Es posible crear un diccionario especificando sus claves y un único valor de «relleno»:
>>> dict.fromkeys(aeiou, 0)
{a: 0, e: 0, i: 0, o: 0, u: 0}
Nota: Es válido pasar cualquier «iterable» como referencia a las claves.
5.3. Diccionarios 181
Aprende Python
5.3.3 Operaciones con diccionarios
Obtener un elemento
Para obtener un elemento de un diccionario basta con escribir la clave entre corchetes.
Veamos un ejemplo:
>>> rae = {
... bifronte: De dos frentes o dos caras,
... anarcoide: Que tiende al desorden,
... montuvio: Campesino de la costa
Resumen libro de Sergio Delgado Quintero -119-
... }
>>> rae[anarcoide]
Que tiende al desorden
Si intentamos acceder a una clave que no existe, obtendremos un error:
>>> rae[acceso]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: acceso
Usando get()
Existe una función muy útil para «superar» los posibles errores de acceso por claves
inexistentes. Se trata de get() y su comportamiento es el siguiente:
1. Si la clave que buscamos existe, nos devuelve su valor.
2. Si la clave que buscamos no existe, nos devuelve None4 salvo que le indiquemos otro 119
valor por defecto, pero en ninguno de los dos casos obtendremos un error.
1 >>> rae
2 {bifronte: De dos frentes o dos caras,
3 anarcoide: Que tiende al desorden,
4 montuvio: Campesino de la costa}
5
6 >>> rae.get(bifronte)
7 De dos frentes o dos caras
8
9 >>> rae.get(programación)
10
(continué en la próxima página)
4 None es la palabra reservada en Python para la «nada». Más información en esta web.
182 Capítulo 5. Estructuras de datos
Aprende Python
(proviene de la página anterior)
11 >>> rae.get(programación, No disponible)
12 No disponible
Línea 6: Equivalente a rae[bifronte].
Línea 9: La clave buscada no existe y obtenemos None.5
Línea 11: La clave buscada no existe y nos devuelve el valor que hemos aportado por
defecto.
Añadir o modificar un elemento
Para añadir un elemento a un diccionario sólo es necesario hacer referencia a la clave y
asignarle un valor:
• Si la clave ya existía en el diccionario, se reemplaza el valor existente por el nuevo.
• Si la clave es nueva, se añade al diccionario con su valor. No vamos a obtener un
error a diferencia de las listas.
Partimos del siguiente diccionario para ejemplificar estas acciones:
>>> rae = {
... bifronte: De dos frentes o dos caras,
... anarcoide: Que tiende al desorden,
... montuvio: Campesino de la costa
... }
Vamos a añadir la palabra enjuiciar a nuestro diccionario de la Real Academia de La
Lengua:
>>> rae[enjuiciar] = Someter una cuestión a examen, discusión y juicio
>>> rae
{bifronte: De dos frentes o dos caras,
Resumen libro de Sergio Delgado Quintero -120-
anarcoide: Que tiende al desorden,
montuvio: Campesino de la costa,
enjuiciar: Someter una cuestión a examen, discusión y juicio}
Supongamos ahora que queremos modificar el significado de la palabra enjuiciar por otra
acepción:
5 Realmente no estamos viendo nada en la consola de Python porque la representación
en cadena de texto
es vacía.
5.3. Diccionarios 183
Aprende Python
>>> rae[enjuiciar] = Instruir, juzgar o sentenciar una causa
>>> rae
{bifronte: De dos frentes o dos caras, 120
anarcoide: Que tiende al desorden,
montuvio: Campesino de la costa,
enjuiciar: Instruir, juzgar o sentenciar una causa}
Creando desde vacío
Una forma muy habitual de trabajar con diccionarios es utilizar el patrón creación
partiendo de uno vacío e ir añadiendo elementos poco a poco.
Supongamos un ejemplo en el que queremos construir un diccionario donde las claves
son
las letras vocales y los valores son sus posiciones:
>>> VOWELS = aeiou
>>> enum_vowels = {}
>>> for i, vowel in enumerate(VOWELS):
... enum_vowels[vowel] = i + 1
...
>>> enum_vowels
{a: 1, e: 2, i: 3, o: 4, u: 5}
Nota: Hemos utilizando la función enumerate() que ya vimos para las listas en el
apartado:
Iterar usando enumeración.
Ejercicio
pycheck: cities
Borrar elementos
Para borrar un elemento de un conjunto podemos utilizar la función remove(). Siguiendo
con el ejemplo anterior, vamos a borrar al último «beatle» añadido:
>>> beatles
{Best, Harrison, Lennon, McCartney, Starr}
>>> beatles.remove(Best)
>>> beatles
{Harrison, Lennon, McCartney, Starr}
Longitud de un conjunto
Podemos conocer el número de elementos (cardinalidad) que tiene un conjunto con la
función
len():
>>> beatles
{Harrison, Lennon, McCartney, Starr}
>>> len(beatles)
4
Iterar sobre un conjunto
Tal y como hemos visto para otros tipos de datos iterables, la forma de recorrer los
elementos
de un conjunto es utilizar la sentencia for:
>>> for beatle in beatles:
... print(beatle)
...
Harrison
McCartney
Starr
Lennon
Consejo: Como en el ejemplo anterior, es muy común utilizar una variable en singular
para
Resumen libro de Sergio Delgado Quintero -129-
recorrer un iterable (en plural). No es una regla fija ni sirve para todos los casos, pero sí
suele ser una buena práctica.
Pertenencia de elemento
Al igual que con otros tipos de datos, Python nos ofrece el operador in para determinar si
un elemento pertenece a un conjunto:
>>> beatles
{Harrison, Lennon, McCartney, Starr}
>>> Lennon in beatles
True
>>> Fari in beatles
False
Ordenando un conjunto 129
Ya hemos comentado que los conjuntos no mantienen un orden. ¿Pero qué ocurre si
intentamos ordenarlo?
>>> marks = {8, 4, 6, 2, 9, 5}
>>> sorted(marks)
[2, 4, 5, 6, 8, 9]
Obtenemos una lista con los elementos ordenados.
Hay que tener en cuenta que, lógicamente, no podremos hacer uso de la función sort()
sobre
un conjunto:
>>> marks.sort()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: set object has no attribute sort
131
132
f = open("temperaturas.dat")
print(f)
Resumen libro de Sergio Delgado Quintero -133-
<_io.TextIOWrapper name='temperaturas.dat' mode='r' encoding='cp1252'>
La función open() recibe como primer argumento la ruta al fichero que queremos
manejar (como un «string») y como segundo argumento el modo de apertura (también
como un «string»). Nos devuelve el manejador del fichero, que en este caso lo estamos
asignando a una variable llamada f pero le podríamos haber puesto cualquier otro
nombre.
Nota: Es importante dominar los conceptos de ruta relativa y ruta absoluta para el
trabajo con ficheros. Véase este artículo de DeNovatoANovato.
El manejador del fichero se implementa mediante un flujo de entrada/salida para las
operaciones de lectura/escritura. Este objeto almacena, entre otras cosas, la ruta al
fichero, el modo de apertura y la codificación: 133
Truco: Existen muchas codificaciones de caracteres para ficheros, pero la más utilizada
es UTF-8 ya que es capaz de representar cualquier caracter Unicode al utilizar una
longitud variable de 1 a 4 bytes.
Hay que tener en cuenta que la ruta al fichero que abrimos (en modo lectura) debe
existir, ya que de lo contrario obtendremos un error:
>>> f = open(foo.txt, r)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: foo.txt
Una vez abierto el fichero ya podemos proceder a leer su contenido. Para ello Python nos
ofrece la posibilidad de leer todo el fichero de una vez o bien leerlo línea a línea.
23 31
26 34
23 33
22 29
22 28
22 28
readlines() Devuelve todo el contenido del fichero como una lista (list) donde cada
elemento es una línea:
# Podemos obviar r ya que es el modo por defecto!
f = open("temperaturas.dat")
Resumen libro de Sergio Delgado Quintero -134-
res=f.readlines()
print(res)
['23 29\n', '23 31\n', '26 34\n', '23 33\n', '22 29\n', '22 28\n', '22
28\n']
Importante: Nótese que, en ambos casos, los saltos de línea \n siguen apareciendo en
los datos leídos, por lo que habría que «limpiar» estos caracteres. Para ello se
recomienda utilizar las funciones ya vistas de cadenas de texto. 134
f = open("temperaturas.dat")
for line in f: # that easy!
print(line)
3 31
26 34
23 33
22 29
22 28
22 28
Truco: Igual que pasaba anteriormente, la lectura línea por línea también incluye el salto
de línea \n lo que provoca un «doble espacio» entre cada una de las salidas. Bastaría con
aplicar line.strip() para eliminarlo.
Lectura de una línea
Hay ocasiones en las que nos interesa leer únicamente una sola línea. Es cierto que esto
se puede conseguir mediante la aproximación anterior. Sería algo como:
Resumen libro de Sergio Delgado Quintero -135-
for line in f: # that easy!
print(line)
break
...
23 29
f = open("temperaturas.dat")
res=f.readline()
print(res) 135
23 29\n
Es importante señalar que cuando utilizamos la función readline() el «puntero de
lectura» se desplaza a la siguiente línea del fichero, con lo que podemos seguir
cargando la información según nos interese:
>>> f = open(files/temps.dat)
>>> # Lectura de las 3 primeras líneas
>>> for _ in range(3):
... print(f.readline().strip())
...
23 29
23 31
26 34
>>> # Lectura de las restantes líneas (4)
>>> for line in f:
... print(line.strip())
...
23 33
22 29
22 28
22 28
Los ficheros se agotan
Hay que tener en cuenta que, una vez abierto el fichero, la lectura de su contenido se
puede realizar una única vez. O dicho de otra manera, el iterable que lleva implícito «se
agota».
Veamos este escenario con el ejemplo anterior:
f = open("temperaturas.dat")
for line in f:
print(line.strip(), end="\t")
for line in f:
print(line.strip(), end="\t")
# No hay salida!!
Resumen libro de Sergio Delgado Quintero -136-
Esto mismo ocurre si utilizamos funciones como read() o readlines().
Advertencia: Por este motivo y también por cuestiones de legibilidad del código,
deberíamos abrir un fichero una única vez y realizar todas las operaciones de lectura
necesarias, siempre que las circunstancias lo permitan.
f = open("canary-iata.dat","w")
canary_iata = ("TFN", "TFS", "LPA", "GMZ", "VDE", "SPC", "ACE", "UE")
for code in canary_iata:
f.write(code + "\n")
f.close()
Nótese:
Línea 4 Escritura de cada código en el fichero. La función write() no incluye el salto de
línea por defecto, así que lo añadimos de manera explícita.
Línea 7 Cierre del fichero con la función close(). Especialmente en el caso de la escritura
de ficheros, se recomienda encarecidamente cerrar los ficheros para evitar pérdida de
datos.
Advertencia: Siempre que se abre un fichero en modo escritura utilizando el argumento
w, el fichero se inicializa, borrando cualquier contenido que pudiera tener.
Resumen libro de Sergio Delgado Quintero -137-
Otra forma de escribir la tupla «de una sola vez» podría ser utilizando la función join()
con el salto de línea como separador:
f = open("canary-iata.dat","w")
canary_iata = ("TFN", "TFS", "LPA", "GMZ", "VDE", "SPC", "ACE", "UE")
f.write("\n".join(canary_iata))
f.close()
En el caso de que ya tengamos una lista (iterable) cuyos elementos tengan el formato
de salida que necesitamos (incluyendo salto de línea si así fuera necesario) podemos
utilizar la función writelines() que nos ofrece Python. 137
Siguiendo con el ejemplo anterior, imaginemos un escenario en el que la tupla ya contiene
los saltos de línea:
>>> canary_iata = (TFN\n, TFS\n, LPA\n, GMZ\n, VDE\n, SPC\n,
ACE\n,
˓→FUE\n)
>>> f = open(files/canary-iata.dat, w)
>>> f.writelines(canary_iata)
>>> f.close()
Truco: Esta aproximación puede ser interesante cuando leemos de un fichero y
escribimos
en otro ya que las líneas «vienen» con el salto de línea ya incorporado.
with open("temperaturas.dat") as f:
for line in f:
min_temp, max_temp = line.strip().split()
print(min_temp, max_temp)
Línea 1 Apertura del fichero en modo lectura utilizando el gestor de contexto definido por
la palabra reservada with.
Línea 2 Lectura del fichero línea a línea utilizando la iteración sobre el manejador del
fichero.
Línea 3 Limpieza de saltos de línea con strip() encadenando la función split() para
separar las dos temperaturas por el carácter espacio. Ver limpiar una cadena y dividir
una cadena.
Línea 4 Imprimir por pantalla la temperatura mínima y la máxima.
Resumen libro de Sergio Delgado Quintero -138-
Nota: Es una buena práctica usar with cuando se manejan ficheros. La ventaja es que el
fichero se cierra adecuadamente en cualquier circunstancia, incluso si se produce
cualquier
5.5. Ficheros 211
Aprende Python
tipo de error.
Hay que prestar atención a la hora de escribir valores numéricos en un fichero, ya que el
método write() por defecto espera ver un «string» como argumento:
>>> lottery = [43, 21, 99, 18, 37, 99]
>>> with open(files/lottery.dat, w) as f:
... for number in lottery:
... f.write(number + \n)
... 138
Traceback (most recent call last):
File "<stdin>", line 3, in <module>
TypeError: write() argument must be str, not int
Importante: Para evitar este tipo de errores, se debe convertir a str aquellos valores que
queramos usar con la función write() para escribir información en un fichero de texto. Los
f-strings son tu aliado.
EJERCICIOS DE REPASO
1. pycheck: avg_temps
2. pycheck: wc
3. pycheck: read_csv
4. pycheck: txt2md
5. pycheck: find_words
6. pycheck: sum_matrix
7. pycheck: longest_word
8. pycheck: word_freq
9. pycheck: get_line
10. pycheck: replace_chars
11. pycheck: histogram
12. pycheck: submarine
212 Capítulo 5. Estructuras de datos
Aprende Python
AMPLIAR CONOCIMIENTOS
• Reading and Writing Files in Python
• Python Context Managers and the «with» Statement
CAPÍTULO 6
Modularidad
Resumen libro de Sergio Delgado Quintero -139-
139
6.1 Funciones
El concepto de función es básico en prácticamente cualquier lenguaje de programación.
Se trata de una estructura que nos permite agrupar código. Persigue dos objetivos claros:
1. No repetir fragmentos de código en un programa.
2. Reutilizar el código en distintos escenarios.
Una función viene definida por su nombre, sus parámetros y su valor de retorno. Esta
parametrización de las funciones las convierten en una poderosa herramienta ajustable a
las circunstancias que tengamos. Al invocarla estaremos solicitando su ejecución y
obtendremos unos resultados.1
Para definir una función utilizamos la palabra reservada def seguida del nombre6 de la
función. A continuación aparecerán 0 o más parámetros separados por comas (entre
paréntesis), finalizando la línea con dos puntos : En la siguiente línea empezaría el
cuerpo de la función que puede contener 1 o más sentencias, incluyendo (o no) una
sentencia de retorno con el resultado mediante return.
Resumen libro de Sergio Delgado Quintero -140-
6 Las reglas aplicadas a nombres de variables también se aplican a nombres de
funciones.
140
Advertencia: Prestar especial atención a los dos puntos : porque suelen olvidarse en la
definición de la función.
Hagamos una primera función sencilla que no recibe parámetros:
def say_hello():
print(Hello!)
Nota: Nótese la indentación (sangrado) del cuerpo de la función.
Los nombres de las funciones siguen las mismas reglas que las variables y, como
norma general, se suelen utilizar verbos en infinitivo para su definición: load_data,
store_values, reset_cart, filter_results, block_request, …
Para invocar (o «llamar») a una función sólo tendremos que escribir su nombre seguido
de paréntesis. En el caso de la función sencilla (vista anteriormente) se haría así:
def say_hello():
print("Hello!")
say_hello()
Hello!
Nota: Como era de esperar, al invocar a esta función obtenemos un mensaje por pantalla,
fruto de la ejecución del cuerpo de la función.
Cuando queremos invocar a una función dentro de un fichero *.py lo haremos del
mismo modo que hemos visto en el intérprete interactivo:
def say_hello():
print(Hello!)
Resumen libro de Sergio Delgado Quintero -141-
Retornar un valor
Las funciones pueden retornar (o «devolver») un valor. Veamos un ejemplo muy sencillo:
>>> def one():
... return 1
...
>>> one()
1
Importante: No confundir return con print(). El valor de retorno de una función nos 141
permite usarlo fuera de su contexto. El hecho de añadir print() al cuerpo de una función es
algo «coyuntural» y no modifica el resultado de la lógica interna.
Nota: En la sentencia return podemos incluir variables y expresiones, no únicamente
literales.
Pero no sólo podemos invocar a la función directamente, también la podemos integrar en
otras expresiones. Por ejemplo en condicionales:
>>> if one() == 1:
... print(It works!)
... else:
... print(Something is broken)
...
It works!
Si una función no incluye un return de forma explícita, devolverá None de forma implícita:
Existe la posibilidad de usar la sentencia return «a secas» (que también devuelve None) y
hace que «salgamos» inmediatamente de la función:
>>> def quick():
... return
...
>>> print(quick())
None
Advertencia: En general, esto no se considera una buena práctica salvo que sepamos
lo que estamos haciendo. Si la función debe devolver None es preferible ser explícito y
utilizar return None. Aunque es posible que en ciertos escenarios nos interese dicha
aproximación.
Una función puede retornar más de un valor. El «secreto» es hacerlo mediante una
tupla:
def multiple():
Resumen libro de Sergio Delgado Quintero -142-
return 0, 1 # es una tupla!
result = multiple()
print(result)
print(type(result))
(0, 1)
<class 'tuple'
142
Por lo tanto, podremos aplicar el desempaquetado de tuplas sobre el valor retornado por
la función:
>>> a, b = multiple()
>>> a
0
>>> b
1
6.1.2 Parámetros y argumentos
En este caso escribiremos una función que recibe un valor numérico y devuelve su raíz
cuadrada:
def sqrt(value):
return value ** (1/2)
raiz= sqrt(4)
print(raiz)
2.0
Truco: La sentencia pass permite «no hacer nada». Es una especie de «placeholder».
Veamos otra función con dos parámetros y algo más de lógica de negocio:2
143
>>> _min(7, 9)
7
Nótese que la sentencia return puede escribirse en múltiples ocasiones y puede
encontrarse en cualquier lugar de la función, no necesariamente al final del cuerpo. Esta
técnica puede ser beneficiosa en múltiples escenarios.
Uno de esos escenarios se relaciona con el concepto de cláusula guarda: una pieza de
código que normalmente está al comienzo de la función y comprueba una serie de
condiciones para continuar con la ejecución o cortarla10.
Ejercicio
pycheck: squared_sum
10 Para más información sobre las cláusulas guarda, véase este artículo de Miguel G.
Flores
Argumentos posicionales
144
Lo que ha sucedido es un mapeo directo entre argumentos y parámetros en el mismo
orden que estaban definidos:
Parámetro Argumento
Pero es evidente que una clara desventaja del uso de argumentos posicionales es que se
necesita recordar el orden de los argumentos. Un error en la posición de los argumentos
puede generar resultados indeseados:
res=build_cpu( 8, 2.7,"AMD")
print(res)
Argumentos nominales
En esta aproximación los argumentos no son copiados en un orden específico sino que se
asignan por nombre a cada parámetro. Ello nos permite evitar el problema de conocer
cuál es el orden de los parámetros en la definición de la función. Para utilizarlo, basta con
realizar una asignación de cada argumento en la propia llamada a la función.
Veamos la misma llamada que hemos hecho en el ejemplo de construcción de la «cpu»
pero ahora utilizando paso de argumentos nominales:
res=build_cpu("AMD", 8, 2.7)
print(res)
res=build_cpu( 8, 2.7,"AMD")
print(res)
Resumen libro de Sergio Delgado Quintero -145-
values = [2, 3, 4]
def square_it(values):
# NO HAGAS ESTO
for i in range(len(values)):
values[i] **= 2
return values
resultados=square_it(values)
print(resultados)
print(values)
[4, 9, 16]
[4, 9, 16]
Advertencia: Esto no es una buena práctica. O bien documentar que el argumento
puede modificarse o bien retornar un nuevo valor. Por regla general, no se recomienda
Resumen libro de Sergio Delgado Quintero -146-
que las funciones modifiquen argumentos de entrada, salvo que sea específicamente lo
que estamos buscando.
Es posible especificar valores por defecto en los parámetros de una función. En el caso
de que no se proporcione un valor al argumento en la llamada a la función, el parámetro
correspondiente tomará el valor definido por defecto.
Siguiendo con el ejemplo de la «cpu», podemos asignar 2.0GHz como frecuencia por
defecto.
La definición de la función cambiaría ligeramente:
R2=build_cpu("INTEL", 2, 3.4)
print(R2)
Nivel intermedio
Es importante tener presente que los valores por defecto en los parámetros se calculan
cuando se define la función, no cuando se ejecuta. Veamos un ejemplo siguiendo con el
caso anterior:
>>> DEFAULT_FREQ = 2.0
>>> def build_cpu(vendor, num_cores, freq=DEFAULT_FREQ):
... return dict(
... vendor=vendor,
... num_cores=num_cores,
... freq=freq
... )
...
>>> build_cpu(AMD, 4)
{vendor: AMD, num_cores: 4, freq: 2.0}
>>> DEFAULT_FREQ = 3.5
>>> build_cpu(AMD, 4)
{vendor: AMD, num_cores: 4, freq: 2.0}
Ejercicio
pycheck: factorial
['a']
['b']
['x', 'y', 'z', 'a']
['x', 'y', 'z', 'b']
Aparentemente todo está funcionando de manera correcta, pero veamos qué ocurre en
las siguientes llamadas:
>>> def buggy(arg, result=[]):
... result.append(arg)
... print(result)
...
>>> buggy(a)
[a]
>>> buggy(b) # Se esperaría [b]
[a, b]
Obviamente algo no ha funcionado correctamente. Se esperaría que result tuviera una
lista vacía en cada ejecución. Sin embargo esto no sucede por estas dos razones:
1. El valor por defecto se establece cuando se define la función.
2. La variable result apunta a una zona de memoria en la que se modifican sus valores.
A riesgo de perder el parámetro por defecto, una posible solución sería la siguiente:
>>> def works(arg):
... result = []
... result.append(arg)
... return result
...
>>> works(a)
[a]
>>> works(b)
[b]
Resumen libro de Sergio Delgado Quintero -148-
La forma de arreglar el código anterior utilizando un parámetro con valor por defecto sería
utilizar un tipo de dato inmutable y tener en cuenta cuál es la primera llamada:
>>> def nonbuggy(arg, result=None):
... if result is None:
... result = []
... result.append(arg)
... print(result)
...
>>> nonbuggy(a)
[a]
>>> nonbuggy(b)
[b]
>>> nonbuggy(a, [x, y, z]) 148
[x, y, z, a]
>>> nonbuggy(b, [x, y, z])
[x, y, z, b]
Empaquetar/Desempaquetar argumentos
def sumavariable(*values):
result = 0
for value in values: # values es una tupla
result += value
return result
res= sumavariable(4, 3, 2, 1,3.4)
print(res)
Resumen libro de Sergio Delgado Quintero -149-
13.4
xiste la posibilidad de usar el asterisco * en la llamada a la función para desempaquetar
los argumentos posicionales:
>>> values = (4, 3, 2, 1)
>>> _sum(values)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in _sum
TypeError: unsupported operand type(s) for +=: int and tuple
>>> _sum(*values) # Desempaquetado
10
ef best_student(**marks):
max_mark = -1
for student, mark in marks.items(): # marks es un diccionario
if mark > max_mark:
max_mark = mark
best_student = student
return best_student
print(res1)
inma
ejercicio
nolo cambia
return a,b,n
a=2.9
b=3.1
Resumen libro de Sergio Delgado Quintero -150-
n=4
print(f" antes a ={a} b= {b} n={n}")
ingresar(a,b,n)
print(f" despues a ={a} b= {b} n={n}")
lo cambia
return a,b,n
a=2.9
b=3.1
n=4
print(f" antes a ={a} b= {b} n={n}")
a,b,n=ingresar(a,b,n)
print(f" despues a ={a} b= {b} n={n}")
print(type(success))
def doit(f):
f()
doit(success)
Yeah!
Veamos un segundo ejemplo en el que pasamos, no sólo una función como argumento,
sino los valores con los que debe operar:
print(type(repeat_please))
def doit(f, arg1, arg2):
return f(arg1, arg2)
def cuadrado(x):
return x*x
def cubo(x):
return x*cuadrado(x)
print(cubo(2))
6.1.3 Documentación
154
Ya hemos visto que en Python podemos incluir comentarios para explicar mejor
determinadas zonas de nuestro código.
Del mismo modo podemos (y en muchos casos debemos) adjuntar documentación a la
definición de una función incluyendo una cadena de texto (docstring) al comienzo de su
cuerpo:
def closest_int(value):
"""Returns the closest integer to the given value.
The operation is:
1. Compute distance to floor.
2. If distance less than a half, return floor.
Otherwise, return ceil.
"""
floor = int(value)
if value - floor < 0.5:
return floor
else:
return floor + 1
help(closest_int)
print(help(closest_int))
closest_int(value)
Returns the closest integer to the given value.
The operation is:
1. Compute distance to floor.
2. If distance less than a half, return floor.
Otherwise, return ceil.
Explicación de parámetros
Como ya se ha visto, es posible documentar una función utilizando un docstring. Pero la
redacción y el formato de esta cadena de texto puede ser muy variada. Existen distintas
formas de documentar una función (u otros objetos)3:
reStructuredText docstrings Formato de documentación recomendado por Python.
Google docstrings Formato de documentación recomendado por Google.
3 Véase Docstring Formats.
Nivel avanzado
Funciones interiores
Está permitido definir una función dentro de otra función:
>>> VALID_CHARS = xyz
>>> def validation_rate(text: str) -> float:
... Rate of valid chars in text. 158
... def is_valid_char(char: str) -> bool:
... return char in VALID_CHARS
...
... checklist = [is_valid_char(c) for c in text]
... return sum(checklist) / len(text)
...
>>> validation_rate(zxyzxxyz)
1.0
>>> validation_rate(abzxyabcdz)
0.4
>>> validation_rate(abc)
0.0
Truco: Estas funciones pueden tener sentido cuando su ámbito de aplicación es muy
concreto y no se pueden reutilizar fácilmente.
Clausuras
Una clausura (del término inglés «closure») establece el uso de una función interior que
se genera dinámicamente y recuerda los valores de los argumentos con los que fue
creada:
>>> def make_multiplier_of(n):
... def multiplier(x):
... return x * n
... return multiplier
...
>>> m3 = make_multiplier_of(3)
>>> m5 = make_multiplier_of(5)
>>> type(m3)
function
>>> m3(7) # 7 * 3
21
>>> type(m5)
function
>>> m5(8) # 8 * 5
40
>>> make_multiplier_of(5)(8) # Llamada directa!
40
Importante: En una clausura retornamos una función, no una llamada a una función.
Resumen libro de Sergio Delgado Quintero -159-
Funciones anónimas «lambda»
Una función lambda tiene las siguientes propiedades:
1. Se escribe en una única sentencia (línea).
2. No tiene nombre (anónima).
3. Su cuerpo conlleva un return implícito.
4. Puede recibir cualquier número de parámetros.
Veamos un primer ejemplo de función «lambda» que nos permite contar el número de
palabras de una cadena de texto:
>>> num_words = lambda t: len(t.split())
>>> type(num_words)
function
>>> num_words
<function __main__.<lambda>(t)> 159
>>> num_words(hola socio vamos a ver)
5
Veamos otro ejemplo en el que mostramos una tabla con el resultado de aplicar el «and»
lógico mediante una función «lambda» que ahora recibe dos parámetros:
>>> logic_and = lambda x, y: x & y
>>> for i in range(2):
... for j in range(2):
... print(f{i} & {j} = {logic_and(i, j)})
...
0&0=0
0&1=0
1&0=0
1&1=1
Las funciones «lambda» son bastante utilizadas como argumentos a otras funciones.
Un ejemplo claro de ello es la función sorted que recibe un parámetro opcional key donde
se define la clave de ordenación.
Veamos cómo usar una función anónima «lambda» para ordenar una tupla de pares
longitud-latitud:
>>> geoloc = (
... (15.623037, 13.258358),
(continué en la próxima página)
240 Capítulo 6. Modularidad
Aprende Python
(proviene de la página anterior)
... (55.147488, -2.667338),
... (54.572062, -73.285171),
... (3.152857, 115.327724),
... (-40.454262, 172.318877)
)
>>> # Ordenación por longitud (primer elemento de la tupla)
>>> sorted(geoloc)
[(-40.454262, 172.318877),
(3.152857, 115.327724),
(15.623037, 13.258358),
(54.572062, -73.285171),
(55.147488, -2.667338)]
>>> # Ordenación por latitud (segundo elemento de la tupla)
>>> sorted(geoloc, key=lambda t: t[1])
[(54.572062, -73.285171),
Resumen libro de Sergio Delgado Quintero -160-
(55.147488, -2.667338),
(15.623037, 13.258358),
(3.152857, 115.327724),
(-40.454262, 172.318877)]
Ejercicio
pycheck: sort_ages
Enfoque funcional
Como se comentó en la introducción, Python es un lenguaje de programación
multiparadigma. Uno de los paradigmas menos explotados en este lenguaje es la
programación funcional4.
Python nos ofrece 3 funciones que encajan verdaderamente bien en este enfoque: map(),
filter() y reduce(). 160
4 Definición de Programación funcional en Wikipedia.
reduce()
Para poder usar esta función debemos usar el módulo functools. Nos permite aplicar una
función dada sobre todos los elementos de un iterable de manera acumulativa. O dicho en
otras palabras, nos permite reducir una función sobre un conjunto de valores.
Supongamos que queremos realizar el producto de una serie de valores aplicando este
enfoque:
>>> from functools import reduce
>>> def mult_values(a, b):
... return a * b
...
>>> data = range(1, 6)
>>> reduce(mult_values, data) # ((((1 * 2) * 3) * 4) * 5)
120
Aplicando una función anónima «lambda»…
>>> reduce(lambda x, y: x * y, data)
120
Consejo: Por cuestiones de legibilidad del código, se suelen preferir las listas por
comprensión a funciones como map() o filter(), aunque cada problema tiene sus propias
características y sus soluciones más adecuadas. Es un enfoque «más pitónico».
Hazlo pitónico
Trey Hunner explica en una de sus «newsletters» lo que él entiende por código pitónico:
«Pitónico es un término extraño que significa diferentes cosas para diferentes personas.
Algunas personas piensan que código pitónico va sobre legibilidad. Otras personas
piensan que va sobre adoptar características particulares de Python. Mucha gente tiene
una definición difusa que no va sobre legibilidad ni sobre características del lenguaje.
Yo normalmente uso el término código pitónico como un sinónimo de código idiomático o
la forma en la que la comunidad de Python tiende a hacer las cosas cuando escribe
Python.
Eso deja mucho espacio a la interpretación, ya que lo que hace algo idiomático en Python
no está particularmente bien definido.
Yo argumento que código pitónico implica adoptar el desempaquetado de tuplas, usar
listas por comprensión cuando sea apropiado, usar argumentos nominales cuando tenga
sentido,
evitar el uso excesivo de clases, usar las estructuras de iteración adecuadas o evitar
recorrer mediante índices.
Resumen libro de Sergio Delgado Quintero -162-
Para mí, código pitónico significa intentar ver el código desde la perspectiva de las
herramientas específicas que Python nos proporciona, en oposición a la forma en la que
resolveríamos el mismo problema usando las herramientas que nos proporciona
JavaScript,
Java, C, …»
Generadores
Un generador, como su propio nombre indica, se encarga de generar «valores» sobre los
que podemos iterar. Es decir, no construye una secuencia de forma explícita, sino que
nos permite ir «consumiendo» un valor de cada vez. Esta propiedad los hace idóneos
para situaciones en las que el tamaño de las secuencias podría tener un impacto negativo
en el consumo de memoria.
De hecho ya hemos visto algunos generadores y los hemos usado sin ser del todo 162
conscientes.
Algo muy parecido8 a un generador es range() que ofrece la posibilidad de crear
secuencias de números.
Básicamente existen dos implementaciones de generadores:
• Funciones generadoras.
• Expresiones generadoras.
Importante: A diferencia de las funciones ordinarias, los generadores tienen la capacidad
de «recordar» su estado para recuperarlo en la siguiente iteración y continuar
devolviendo nuevos valores.
Funciones generadoras
Las funciones generadoras9 (o factorías de generadores) se escriben como funciones
ordinarias con el matiz de incorporar la sentencia yield que sustituye, de alguna manera, a
return. Esta sentencia devuelve el valor indicado y, a la vez, «congela» el estado de la
función hasta la siguiente llamada.
Expresiones generadoras
Una expresión generadora es sintácticamente muy similar a una lista por comprensión,
pero utilizamos paréntesis en vez de corchetes. Se podría ver como una versión acortada
de una función generadora.
Decoradores
Hay situaciones en las que necesitamos modificar el comportamiento de funciones
existentes pero sin alterar su código. Para estos casos es muy útil usar decoradores.
Un decorador es una función que recibe como parámetro una función y devuelve otra
función. Se podría ver como un caso particular de clausura.
El esqueleto básico de un decorador es el siguiente:
>>> def my_decorator(func):
... def wrapper(*args, **kwargs):
... # some code before calling func
... return func(*args, **kwargs)
... # some code after calling func
... return wrapper
...
Resumen libro de Sergio Delgado Quintero -164-
164
Manipulando argumentos
Hemos visto un ejemplo de decorador que trabaja sobre el resultado de la función
decorada, pero nada impide que trabajemos sobre los argumentos que se le pasa a la
función decorada.
Supongamos un escenario en el que implementamos funciones que trabajan con dos
operandos y queremos asegurarnos de que esos operados son números enteros. Lo
primero será definir el decorador:
Múltiples decoradores
Resumen libro de Sergio Delgado Quintero -166-
Podemos aplicar más de un decorador a cada función. Para ejemplificarlo vamos a crear
dos decoradores muy sencillos:
>>> def plus5(func):
... def wrapper(*args, **kwargs):
... result = func(*args, **kwargs)
... return result + 5
... return wrapper
(continué en la próxima página)
250 Capítulo 6. Modularidad
Aprende Python
(proviene de la página anterior)
...
>>> def div2(func): 166
... def wrapper(*args, **kwargs):
... result = func(*args, **kwargs)
... return result // 2
... return wrapper
...
Ahora aplicaremos ambos decoradores sobre una función que realiza el producto de dos
números:
>>> @plus5
... @div2
... def prod(a, b):
... return a * b
...
>>> prod(4, 3)
11
>>> ((4 * 3) // 2) + 5
11
Cuando tenemos varios decoradores, se aplican desde afuera hacia adentro (modelo
capa de cebolla). Eso sí, hay que tener en cuenta que la ejecución de un decorador puede
depender de otro decorador.
Si anotamos los decoradores podemos ver exactamente cuál es el orden de ejecución:
>>> def plus5(func):
... def wrapper(*args, **kwargs):
... result = func(*args, **kwargs) # ——————┐
... print(f{result=}) # |
... print(plus5) # |
... return result + 5 # |
... return wrapper # |
... # |
... # |
... def div2(func): # |
... def wrapper(*args, **kwargs): # |
... result = func(*args, **kwargs) # ◄—————┘
... print(f{result=})
... print(div2)
... return result // 2
... return wrapper
6.1. Funciones 251
Aprende Python
Ahora ejecutamos la función decorada:
Resumen libro de Sergio Delgado Quintero -167-
>>> prod(4, 3)
result=12 # función prod "tal cual" (4*3)
div2 # decorador div2
result=6 # aplicación decorador div2 (12/2)
plus5 # decorador plus5
11 # aplicación decorador plus5 (6+5)
Decoradores con parámetros
El último «salto mortal» sería definir decoradores con parámetros. El esqueleto básico de
un
decorador con parámetros es el siguiente:
>>> def my_decorator_with_params(*args, **kwargs):
... def decorator(func):
... def wrapper(*args, **kwargs): 167
... return func(*args, **kwargs)
... return wrapper
... return decorator
...
Lo más sencillo es verlo con un ejemplo. Supongamos que queremos forzar a que los
parámetros de entrada a la función sean de un tipo concreto pero parametrizable.
Podríamos definir el decorador de la siguiente manera:
>>> def assert_type(atype):
... def decorator(func):
... def wrapper(*args, **kwargs):
... all_args_with_atype = all(isinstance(a, atype) for a in args)
... all_kwargs_with_atype = all(isinstance(a, atype) for a in kwargs.
˓→values())
... if all_args_with_atype and all_kwargs_with_atype:
... return func(*args, **kwargs)
... return None
... return wrapper
... return decorator
...
Ahora creamos una función sencilla que suma dos números y le aplicamos el decorador:
>>> @assert_type(float)
... def _sum(a, b):
... return a + b
...
Veamos el comportamiento para diferentes casos de uso:
>>> result = _sum(3, 4)
>>> print(result)
None
>>> result = _sum(3.0, 4.0)
>>> print(result)
7.0
>>> result = _sum(a=3.0, b=4.0) # Llamada con kwargs
>>> print(result)
7.0
La ventaja que tiene este enfoque es que podemos aplicar «distintos» decoradores
modificando sus parámetros. Por ejemplo, supongamos que ahora queremos asegurar
que
una función trabaja únicamente con cadenas de texto:
>>> @assert_type(str)
Resumen libro de Sergio Delgado Quintero -168-
... def split(text):
... half_size = len(text) // 2
... return text[:half_size], text[half_size:]
...
Veamos su aplicación con distintos tipos de datos:
>>> result = split(bienvenida)
>>> print(result)
(bienv, enida)
>>> result = split(256)
>>> print(result)
None
>>> result = split([10, 20, 30, 40])
>>> print(result) 168
None
Ejercicio
¿Sabría implementar un decorador para ordenar el resultado de cualquier función
tomando
un parámetro opcional que indique si la ordenación es ascendente o descendente?
Funciones recursivas
Advertencia: El uso de global no se considera una buena práctica ya que puede inducir
a confusión y tener efectos colaterales indeseados.
Contenido de los espacios de nombres
Python proporciona dos funciones para acceder al contenido de los espacios de nombres:
locals() Devuelve un diccionario con los contenidos del espacio de nombres local:
>>> language = castellano
>>> def catalonia():
... language = catalan
... print(f{locals()=})
...
>>> catalonia()
locals()={language: catalan}
globals() Devuelve un diccionario con los contenidos del espacio de nombres global:
6.1. Funciones 257
Aprende Python
>>> globals()
{__name__: __main__,
__doc__: Automatically created module for IPython interactive environment,
__package__: None,
__loader__: None,
__spec__: None,
__builtin__: <module builtins (built-in)>,
__builtins__: <module builtins (built-in)>,
Resumen libro de Sergio Delgado Quintero -171-
_ih: [,
"language = castellano",
"def catalonia():\n language = catalan\n print(f{locals()=})\n ",
language,
catalonia(),
globals()],
_oh: {3: castellano},
_dh: [/Users/sdelquin],
In: [,
"language = castellano",
"def catalonia():\n language = catalan\n print(f{locals()=})\n ",
language,
catalonia(), 171
globals()],
Out: {3: castellano},
get_ipython: <bound method InteractiveShell.get_ipython of <IPython.terminal.
˓→interactiveshell.TerminalInteractiveShell object at 0x10e70c2e0>>,
exit: <IPython.core.autocall.ExitAutocall at 0x10e761070>,
quit: <IPython.core.autocall.ExitAutocall at 0x10e761070>,
_: castellano,
__: ,
___: ,
Prompts: IPython.terminal.prompts.Prompts,
Token: Token,
MyPrompt: __main__.MyPrompt,
ip: <IPython.terminal.interactiveshell.TerminalInteractiveShell at␣
˓→0x10e70c2e0>,
_i: catalonia(),
_ii: language,
_iii: "def catalonia():\n language = catalan\n print(f{locals()=})\
˓→n ",
_i1: "language = castellano",
language: castellano,
_i2: "def catalonia():\n language = catalan\n print(f{locals()=})\
˓→n ",
catalonia: <function __main__.catalonia()>,
_i3: language,
_3: castellano,
(continué en la próxima página)
EJERCICIOS DE REPASO
1. pycheck: num_in_interval
2. pycheck: extract_evens
3. pycheck: split_case
4. pycheck: perfect
5. pycheck: palindrome
6. pycheck: count_vowels
7. pycheck: pangram
Resumen libro de Sergio Delgado Quintero -172-
8. pycheck: cycle_alphabet
9. pycheck: bubble_sort
10. pycheck: consecutive_seq
11. pycheck: magic_square
12. pycheck: sum_nested
AMPLIAR CONOCIMIENTOS
• Comparing Python Objects the Right Way: «is» vs «==»
• Python Scope & the LEGB Rule: Resolving Names in Your Code
• Defining Your Own Python Function
• Null in Python: Understanding Python’s NoneType Object
• Python “!=” Is Not “is not”: Comparing Objects in Python
• Python args and kwargs: Demystified 172
• Documenting Python Code: A Complete Guide
• Thinking Recursively in Python
• How to Use Generators and yield in Python
• How to Use Python Lambda Functions
6.1. Funciones 259
Aprende Python
• Python Decorators 101
• Writing Comments in Python
• Introduction to Python Exceptions
• Primer on Python Decorators
Hasta ahora hemos estado usando objetos de forma totalmente transparente, casi sin ser
conscientes de ello. Pero, en realidad, todo en Python es un objeto, desde números a
funciones. El lenguaje provee ciertos mecanismos para no tener que usar explícitamente
técnicas de orientación a objetos.
Llegados a este punto, investigaremos en profundidad sobre la creación y manipulación
de clases y objetos, y todas las operaciones que engloban este paradigma.1
Resumen libro de Sergio Delgado Quintero -173-
¿Qué es un objeto?
Un objeto es una estructura de datos personalizada que contiene datos y código:
Un objeto representa una instancia única de alguna entidad a través de los valores de sus atributos
e interactuan con otros objetos (o consigo mismos) a través de sus métodos.
Resumen libro de Sergio Delgado Quintero -174-
174
175
Para ello usaremos la palabra reservada class seguido del nombre de la clase:
>>> class StarWarsDroid:
... pass
...
Consejo: Los nombres de clases se suelen escribir en formato CamelCase y en
singular3.
Existen multitud de droides en el universo StarWars. Una vez que hemos definido la clase
genérica podemos crear instancias/objetos (droides) concretos:
class StarWarsDroid:
pass
c3po = StarWarsDroid()
r2d2 = StarWarsDroid()
bb8 = StarWarsDroid()
print(c3po)
print(dir(c3po))
print(type(c3po))
salida
__main__.StarWarsDroid
>>> type(bb8)
__main__.StarWarsDroid
Añadiendo atributos
Un atributo no es más que una variable, un nombre al que asignamos un valor, con la
particularidad de vivir dentro de una clase o de un objeto.
Los atributos – por lo general – se suelen asignar durante la creación de un objeto, pero 176
también es posible añadirlos a posteriori:
class StarWarsDroid:
pass
blue_droid = StarWarsDroid()
golden_droid = StarWarsDroid()
golden_droid.name = "C-3PO"
blue_droid.name = "R2-D2"
blue_droid.height = 1.09
blue_droid.num_feet = 3
blue_droid.partner_droid = golden_droid # otro droide como atributo
print(blue_droid.name)
Hemos definido un droide «socio». Veremos a continuació que podemos trabajar con é de
una manera totalmente natural:
>>> type(blue_droid.partner_droid)
__main__.StarWarsDroid
>>> blue_droid.partner_droid.name # acceso al nombre del droide socio
C-3PO
>>> blue_droid.partner_droid.num_feet # aún sin definir!
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: StarWarsDroid object has no attribute num_feet
>>> blue_droid.partner_droid.num_feet = 2
Añadiendo métodos
Un método es una función que forma parte de una clase o de un objeto. En su ámbito
tiene acceso a otros métodos y atributos de la clase o del objeto al que pertenece.
La definición de un método (de instancia) es análoga a la de una función ordinaria, pero
incorporando un primer parámetro self que hace referencia a la instancia actual del objeto.
Una de las acciones más sencillas que se pueden hacer sobre un droide es encenderlo o
apagarlo. Vamos a implementar estos dos métodos en nuestra clase:
class Droid:
Resumen libro de Sergio Delgado Quintero -177-
def switch_on(self):
print("Hi! Im a droid. Can I help you?")
def switch_off(self):
print("Bye! Im going to sleep")
k2so = Droid()
k2so.switch_on()
k2so.switch_off()
Inicialización
Existe un método especial que se ejecuta cuando creamos una instancia de un objeto.
Este método es __init__ y nos permite asignar atributos y realizar operaciones con el
objeto en el momento de su creación. También es ampliamente conocido como el
constructor.
6.2.3 Atributos
Acceso directo
Propiedades
Como hemos visto previamente, los atributos definidos en un objeto son accesibles
públicamente. Esto puede parecer extraño a personas desarrolladoras de otros lenguajes.
En Python existe un cierto «sentido de responsabilidad» a la hora de programar y manejar
este tipo de situaciones.
Una posible solución «pitónica» para la privacidad de los atributos es el uso de
propiedades.
La forma más común de aplicar propiedades es mediante el uso de decoradores:
• @property para leer el valor de un atributo.
• @name.setter para escribir el valor de un atributo.
Veamos un ejemplo en el que estamos ofuscando el nombre del droide a través de
propiedades:
class Droid:
def __init__(self, name):
self.hidden_name = name
@property
def name(self):
print(" inside the getter" )
return self.hidden_name
@name.setter
def name(self, name):
print(" inside the setter" )
self.hidden_name = name
Resumen libro de Sergio Delgado Quintero -179-
droid = Droid("N1-G3L" )
droid.name
droid.name = " Nigel"
Valores calculados
Una propiedad también se puede usar para devolver un valor calculado (o computado).
A modo de ejemplo, supongamos que la altura del periscopio de los droides
astromecánicos
se calcula siempre como un porcentaje de su altura. Veamos cómo implementarlo:
>>> class AstromechDroid:
... def __init__(self, name, height):
... self.name = name
... self.height = height
...
... @property
... def periscope_height(self):
... return 0.3 * self.height
...
>>> droid = AstromechDroid(R2-D2, 1.05)
>>> droid.periscope_height # podemos acceder como atributo
0.315
>>> droid.periscope_height = 10 # no podemos modificarlo
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: cant set attribute
Consejo: La ventaja de usar valores calculados sobre simples atributos es que el cambio
de valor en un atributo no asegura que actualicemos otro atributo, y además siempre
podremos modificar directamente el valor del atributo, con lo que podríamos obtener
efectos colaterales indeseados.
Ocultando atributos
Python tiene una convención sobre aquellos atributos que queremos hacer «privados» (u
ocultos): comenzar el nombre con doble subguión __
Atributos de clase
Podemos asignar atributos a las clases y serán heredados por todos los objetos
instanciados de esa clase.
A modo de ejemplo, en un principio, todos los droides están diseñados para que
obedezcan a su dueño. Esto lo conseguiremos a nivel de clase, salvo que ese
comportamiento se
sobreescriba:
>>> class Droid:
... obeys_owner = True # obedece a su dueño
...
(continué en la próxima página)
(proviene de la página anterior)
>>> good_droid = Droid()
>>> good_droid.obeys_owner
True
>>> t1000 = Droid()
>>> t1000.obeys_owner = False # T-1000 (Terminator)
>>> t1000.obeys_owner
False
>>> Droid.obeys_owner # el cambio no afecta a nivel de clase
True
6.2.4 Métodos
Métodos de instancia
Un método de instancia es un método que modifica el comportamiento del objeto al que
hace referencia. Recibe self como primer parámetro, el cual se convierte en el propio
objeto sobre el que estamos trabajando. Python envía este argumento de forma
transparente.
Veamos un ejemplo en el que, además del constructor, creamos un método de instancia
para desplazar un droide:
>>> class Droid:
... def __init__(self, name): # método de instancia -> constructor
... self.name = name
... self.covered_distance = 0
...
... def move_up(self, steps): # método de instancia
... self.covered_distance += steps
Resumen libro de Sergio Delgado Quintero -181-
... print(fMoving {steps} steps)
...
>>> droid = Droid(C1-10P)
>>> droid.move_up(10)
Moving 10 steps
Métodos de clase
Un método de clase es un método que modifica el comportamiento de la clase a la que
hace referencia. Recibe cls como primer parámetro, el cual se convierte en la propia
clase sobre la que estamos trabajando. Python envía este argumento de forma
transparente.
La identificación de estos métodos se completa aplicando el decorador @classmethod a
la función. 181
Veamos un ejemplo en el que implementaremos un método de clase que lleva la cuenta
de los droides que hemos creado:
Métodos estáticos
Un método estático es un método que no modifica el comportamiento del objeto ni de la
clase. No recibe ningún parámetro especial. La identificación de estos métodos se
completa
aplicando el decorador @staticmethod a la función.
Veamos un ejemplo en el que creamos un método estático para devolver las categorías
de
droides que existen en StarWars:
>>> class Droid:
... def __init__(self):
... pass
...
... @staticmethod
... def get_droids_categories():
... return [Messeger, Astromech, Power, Protocol]
...
>>> Droid.get_droids_categories()
[Messeger, Astromech, Power, Protocol]
Métodos mágicos
Resumen libro de Sergio Delgado Quintero -182-
Nivel avanzado
Cuando escribimos hello world * 3 ¿cómo sabe el objeto hello world lo que debe
hacer para multiplicarse con el objeto entero 3? O dicho de otra forma, ¿cuál es la
implementación
del operador * para «strings» y enteros? En valores numéricos puede parecer evidente
(siguiendo los operadores matemáticos), pero no es así para otros objetos. La solución
que proporciona Python para estas (y otras) situaciones son los métodos mágicos.
Los métodos mágicos empiezan y terminan por doble subguión __ (es por ello que
también se les conoce como «dunder-methods»). Uno de los «dunder-methods» más
famosos es el constructor de una clase: __init__().
Importante: Digamos que los métodos mágicos se «disparan» de manera transparente
cuando utilizamos ciertas estructuras y expresiones del lenguaje.
Para el caso de los operadores, existe un método mágico asociado (que podemos 182
personalizar).
Por ejemplo la comparación de dos objetos se realiza con el método __eq__():
Extrapolando esta idea a nuestro universo StarWars, podríamos establecer que dos
droides son iguales si su nombre es igual, independientemente de que tengan distintos
números de serie:
>>> class Droid:
... def __init__(self, name, serial_number):
... self.serial_number = serial_number
... self.name = name
...
... def __eq__(self, droid):
... return self.name == droid.name
...
>>> droid1 = Droid(C-3PO, 43974973242)
>>> droid2 = Droid(C-3PO, 85094905984)
>>> droid1 == droid2 # llamada implícita a __eq__
True
>>> droid1.__eq__(droid2)
True
Nota: Los métodos mágicos no sólo están restringidos a operadores de comparación o
matemáticos. Existen muchos otros en la documentación oficial de Python, donde son
Resumen libro de Sergio Delgado Quintero -183-
llamados métodos especiales.
Veamos un ejemplo en el que «sumamos» dos droides. Esto se podría ver como una
fusión.
Supongamos que la suma de dos droides implica: a) que el nombre del droide resultante
es la concatenación de los nombres de los droides; b) que la energía del droide resultante
es la suma de la energía de los droides:
>>> class Droid:
... def __init__(self, name, power):
... self.name = name
... self.power = power
...
... def __add__(self, droid):
... new_name = self.name + - + droid.name 183
... new_power = self.power + droid.power
... return Droid(new_name, new_power) # Hay que devolver un objeto de tipo␣
˓→Droid
...
>>> droid1 = Droid(C3PO, 45)
>>> droid2 = Droid(R2D2, 91)
(continué en la próxima página)
Gestores de contexto
Otra de las aplicaciones de los métodos mágicos (especiales) que puede ser interesante
es la de gestores de contexto. Se trata de un bloque de código en Python que engloba
una serie de acciones a la entrada y a la salida del mismo.
Hay dos métodos que son utilizados para implementar los gestores de contexto:
__enter__() Acciones que se llevan a cabo al entrar al contexto.
__exit__() Acciones que se llevan a cabo al salir del contexto.
Veamos un ejemplo en el que implementamos un gestor de contexto que mide tiempos de
ejecución:
>>> from time import time
Resumen libro de Sergio Delgado Quintero -185-
>>> class Timer():
... def __enter__(self):
... self.start = time()
...
... def __exit__(self, exc_type, exc_value, exc_traceback):
... # Omit exception handling
... self.end = time()
... exec_time = self.end - self.start
... print(fExecution time (seconds): {exec_time:.5f})
...
Ahora podemos probar nuestro gestor de contexto con un ejemplo concreto. La forma de
«activar» el contexto es usar la sentencia with seguida del símbolo que lo gestiona:
6.2. Objetos y Clases 277 185
Aprende Python
>>> with Timer():
... for _ in range(1_000_000):
... x = 2 ** 20
...
Execution time (seconds): 0.05283
>>> with Timer():
... x = 0
... for _ in range(1_000_000):
... x += 2 ** 20
...
Execution time (seconds): 0.08749
6.2.5 Herencia
Nivel intermedio
La herencia consiste en crear una nueva clase partiendo de una clase existente, pero
que añade o modifica ciertos aspectos. Se considera una buena práctica tanto para
reutilizar código como para realizar generalizaciones.
Añadir un método
La clase derivada también puede añadir métodos que no estaban presentes en su clase 187
base.
En el siguiente ejemplo vamos a añadir un método translate() que permita a los droides
de protocolo traducir cualquier mensaje:
>>> class Droid:
... def switch_on(self):
... print("Hi! Im a droid. Can I help you?")
...
... def switch_off(self):
... print("Bye! Im going to sleep")
...
>>> class ProtocolDroid(Droid):
... def switch_on(self):
... print("Hi! Im a PROTOCOL droid. Can I help you?")
...
... def translate(self, msg, from_language):
... Translate from language to Human understanding
... print(f{msg} means "ZASCA" in {from_language})
>>> r2d2 = Droid()
>>> c3po = ProtocolDroid()
>>> c3po.translate(kiitos, Huttese) # idioma de Watoo
kiitos means "ZASCA" in Huttese
>>> r2d2.translate(kiitos, Huttese) # droide genérico no puede traducir
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: Droid object has no attribute translate
Con esto ya hemos aportado una personalidad diferente a los droides de protocolo, a
pesar de que heredan de la clase genérica de droides de StarWars.
Puede darse la situación en la que tengamos que acceder desde la clase derivada a
métodos o atributos de la clase base. Python ofrece super() como mecanismo para ello.
Veamos un ejemplo más elaborado con nuestros droides:
>>> class Droid:
... def __init__(self, name):
... self.name = name
(continué en la próxima página)
6.2. Objetos y Clases 281
Aprende Python
Resumen libro de Sergio Delgado Quintero -188-
(proviene de la página anterior)
...
>>> class ProtocolDroid(Droid):
... def __init__(self, name, languages):
... super().__init__(name) # llamada al constructor de la clase base
... self.languages = languages
...
>>> droid = ProtocolDroid(C-3PO, [Ewokese, Huttese, Jawaese])
>>> droid.name # fijado en el constructor de la clase base
C-3PO
>>> droid.languages # fijado en el constructor de la clase derivada
[Ewokese, Huttese, Jawaese]
Herencia múltiple 188
Nivel avanzado
Aunque no está disponible en todos los lenguajes de programación, Python sí permite que
los objetos pueden heredar de múltiples clases base.
Si en una clase se hace referencia a un método o atributo que no existe, Python lo
buscará
en todas sus clases base. Es posible que exista una colisión en caso de que el método o
el atributo buscado esté, a la vez, en varias clases base. En este caso, Python resuelve el
conflicto a través del orden de resolución de métodos4.
Supongamos que queremos modelar la siguiente estructura de clases con herencia
múltiple:
>>> class Droid:
... def greet(self):
... return Here a droid
...
>>> class ProtocolDroid(Droid):
... def greet(self):
... return Here a protocol droid
...
>>> class AstromechDroid(Droid):
... def greet(self):
(continué en la próxima página)
4 Viene del inglés «method resolution order» o mro.
5 Imágenes de los droides por StarWars Fandom.
189
191
Salida esperada:
/home/python/vanrossum.mp4 [size=19B] # self.info() de File
Codec: h264 # ┐
Geolocalization: (23.5454, 31.4343) # ├ self.info() de MediaFile
Duration: 487s # ┘
Dimensions: (1920, 1080) # self.info() de VideoFile
El método size() debe devolver el número total de caracteres sumando las longitudes
de los elementos del atributo contents.
Agregación y composición
Aunque la herencia de clases nos permite modelar una gran cantidad de casos de uso en
Resumen libro de Sergio Delgado Quintero -192-
términos de «is-a» (es un), existen muchas otras situaciones en las que la agregación o
la composición son una mejor opción. En este caso una clase se compone de otras cases:
hablamos de una relación «has-a» (tiene un).
Hay una sutil diferencia entre agregación y composición:
• La composición implica que el objeto utilizado no puede «funcionar» sin la presencia
de su propietario.
• La agregación implica que el objeto utilizado puede funcionar por sí mismo.
192
EJERCICIOS DE REPASO
1. Escriba una clase en Python para representar una secuencia de ADN. De
momento, la • 4 atributos de clase, cada uno representando una base nitrogenada con su
valor como un carácter.
Resumen libro de Sergio Delgado Quintero -193-
• Constructor que recibe una secuencia de caracteres (bases).
• Método para representar el objeto en formato «string».
2. Continúe con el ejercicio anterior, y añada a la clase 4 propiedades que calculen el
número total de cada una de las bases presentes en la secuencia.
3. Continúe con el ejercicio anterior, y añada a la clase un método de instancia para
sumar
dos secuencias de ADN. La suma se hará base a base y el resultado será el máximo de
cada letra(base).
4. Continúe con el ejercicio anterior, y añada a la clase un método de instancia para
obtener el porcentaje de aparición de cada base (usando las propiedades definidas en
ejercicios anteriores).
5. Continúe con el ejercicio anterior, y añada a la clase un método de instancia para
multiplicar dos secuencias de ADN. La multiplicación consiste en dar como salida una 193
nueva secuencia que contenga sólo aquellas bases que coincidan en posición en ambas
secuencias de entrada.
288 Capítulo 6. Modularidad
Aprende Python
→ Solución a todos los ejercicios
AMPLIAR CONOCIMIENTOS
• Supercharge Your Classes With Python super()
• Inheritance and Composition: A Python OOP Guide
• OOP Method Types in Python: @classmethod vs @staticmethod vs Instance Methods
• Intro to Object-Oriented Programming (OOP) in Python
• Pythonic OOP String Conversion: __repr__ vs __str__
• @staticmethod vs @classmethod in Python
• Modeling Polymorphism in Django With Python
• Operator and Function Overloading in Custom Python Classes
• Object-Oriented Programming (OOP) in Python 3
• __________Why Bother Using Property Decorators in Python?
6.3 Excepciones
Especificando excepciones
En el siguiente ejemplo mejoraremos el código anterior, capturando distintos tipos de
excepciones:
• TypeError por si los operandos no permiten la división.
• ZeroDivisionError por si el denominador es cero.
• Exception para cualquier otro error que se pueda producir.
Veamos su implementación:
>>> def intdiv(a, b):
... try:
... result = a // b
... except TypeError:
Resumen libro de Sergio Delgado Quintero -195-
... print(Check operands. Some of them seems strange...)
... except ZeroDivisionError:
... print(Please do not divide by zero...)
... except Exception:
... print(Ups. Something went wrong...)
...
>>> intdiv(3, 0)
Please do not divide by zero...
>>> intdiv(3, 0)
Check operands. Some of them seems strange...
Importante: Las excepciones predefinidas en Python no hace falta importarlas
previamente. Se pueden usar directamente.
Cubriendo más casos 195
Python proporciona la cláusula else para saber que todo ha ido bien y que no se ha
lanzado ninguna excepción. Esto es relevante a la hora de manejar los errores.
De igual modo, tenemos a nuestra disposición la cláusula finally que se ejecuta siempre,
independientemente de si ha habido o no ha habido error.
Veamos un ejemplo de ambos:
6.3. Excepciones 291
Aprende Python
>>> values = [4, 2, 7]
>>> user_index = 3
>>> try:
... r = values[user_index]
... except IndexError:
... print(Error: Index not in list)
... else:
... print(fYour wishes are my command: {r})
... finally:
... print(Have a good day!)
...
Error: Index not in list
Have a good day!
>>> user_index = 2
>>> try:
... r = values[user_index]
... except IndexError:
... print(Error: Index not in list)
... else:
... print(fYour wishes are my command: {r})
... finally:
... print(Have a good day!)
...
Your wishes are my command: 7
Have a good day!
Ejercicio
Sabiendo que ValueError es la excepción que se lanza cuando no podemos convertir una
cadena de texto en su valor numérico, escriba una función get_int() que lea un valor
entero
del usuario y lo devuelva, iterando mientras el valor no sea correcto.
Ejecución a modo de ejemplo:
Give me an integer number: ten
Resumen libro de Sergio Delgado Quintero -196-
Not a valid integer. Try it again!
Give me an integer number: diez
Not a valid integer. Try it again!
Give me an integer number: 10
Trate de implementar tanto la versión recursiva como la versión iterativa.
>>> result = -1
>>> assert result > 0
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
<ipython-input-29-e2efe60b0c46> in <module>
----> 1 assert result > 0
AssertionError:
Podemos observar que la excepción que se lanza no contiene ningún mensaje
informativo. Es
posible personalizar este mensaje añadiendo un segundo elemento en la tupla de la
aserción:
>>> assert result > 0, El resultado debe ser positivo
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
<ipython-input-31-f58052ce672b> in <module>
----> 1 assert result > 0, El resultado debe ser positivo
AssertionError: El resultado debe ser positivo
AMPLIAR CONOCIMIENTOS
• Python Exceptions: An introduction
• Python KeyError Exceptions and How to Handle Them
• Understanding the Python Traceback
6.4 Módulos
Resumen libro de Sergio Delgado Quintero -198-
198
Escribir pequeños trozos de código puede resultar interesante para realizar determinadas
pruebas. Pero a la larga, nuestros programas tenderán a crecer y será necesario agrupar
el código en unidades manejables.
Los módulos son simplemente ficheros de texto que contienen código Python y
representan unidades con las que evitar la repetición y favorecer la reutilización.1
en castellano
def suma(a, b):
Resumen libro de Sergio Delgado Quintero -199-
return a + b
def resta(a, b):
return a - b
def producto(a, b):
return a * b
def division(a, b):
return a / b
Desde otro fichero - en principio en la misma carpeta - podríamos hacer uso de las
funciones definidas en arith.py.
Importar módulo completo 199
Desde otro fichero haríamos lo siguiente para importar todo el contenido del módulo arith.
py:
import arith
print(arith.addere(3, 7))
print(arith.partitus(3, 7))
10
0.42857142857142855
Nota: Nótese que en la línea 3 debemos anteponer a la función sum() el espacio de
nombres que define el módulo arith.
import sys
print(sys.path)
La cadena vacía que existe al final de la lista hace referencia a la carpeta actual.
Modificando directamente la lista sys.path Para ello accedemos a lista que está en el
módulo sys de la librería estandar:
>>> sys.path.append(/tmp) # añadimos al final
>>> sys.path
[/path/to/.pyenv/versions/3.9.1/envs/aprendepython/bin,
/path/to/.pyenv/versions/3.9.1/lib/python3.9,
/path/to/.pyenv/versions/3.9.1/envs/aprendepython/lib/python3.9/site-packages,
,
/tmp]
>>> sys.path.insert(0, /tmp) # insertamos por el principio 200
>>> sys.path
[/tmp,
/path/to/.pyenv/versions/3.9.1/envs/aprendepython/bin,
/path/to/.pyenv/versions/3.9.1/lib/python3.9,
/path/to/.pyenv/versions/3.9.1/envs/aprendepython/lib/python3.9/site-packages,
]
Es posible que no necesitemos todo aquello que está definido en arith.py. Supongamos
que sólo vamos a realizar divisiones. Para ello haremos lo siguiente:
2.5
Nota: Nótese que en la línea 3 ya podemos hacer uso directamente de la función división
porque la hemos importado directamente. Este esquema tiene el inconveniente de la
posible colisión de nombres, en aquellos casos en los que tuviéramos algún objeto con
el mismo nombre que el objeto que estamos importando.
Hay ocasiones en las que interesa, por colisión de otros nombres o por mejorar la
legibilidad, usar un nombre diferente del módulo (u objeto) que estamos importando.
Python nos ofrece esta posibilidad a través de la sentencia as.
Supongamos que queremos importar la función del ejemplo anterior pero con otro
nombre:
2.5
6.4.2 Paquetes
Un paquete es simplemente una carpeta que contiene ficheros .py. Además permite
tener una jerarquía con más de un nivel de subcarpetas anidadas. Para ejemplificar este
modelo vamos a crear un paquete llamado mymath que contendrá 2
módulos:
• arith.py para operaciones aritméticas (ya visto anteriormente).
• logic.py para operaciones lógicas.
El código del módulo de operaciones lógicas es el siguiente:
logic.py
201
def et(a, b):
# Logic "and" of input values
return a & b
def uel(a, b):
# Logic "or" of input values
return a | b
def vel(a, b):
# Logic "xor" of input values
return a ^ b
7 1 directory, 3 files
$ python main.py
if __name__ == __main__
1 import blabla
2
3
4 def myfunc():
5 print(Inside myfunc)
6 blabla.hi()
7
8
9 if __name__ == __main__:
10 print(Entry point) 203
11 myfunc()
import hello El código se ejecuta siempre desde la primera instrucción a la última:
• Línea 1: se importa el módulo blabla.
• Línea 4: se define la función myfunc() y estará disponible para usarse.
AMPLIAR CONOCIMIENTOS
• Defining Main Functions in Python
• Python Modules and Packages: An Introduction
• Absolute vs Relative Imports in Python
• Running Python Scripts
• Writing Beautiful Pythonic Code With PEP 8
• Python Imports 101
• Clean Code in Python
Procesamiento de texto
Además de las herramientas que se han visto en cadenas de texto, la librería estándar
nos ofrece una serie de módulos para procesamiento de texto que nos harán la vida más
fácil a la hora de gestionar este tipo de datos.
7.1 string
Resumen libro de Sergio Delgado Quintero -204-
204
El módulo string proporciona una serie de constantes muy útiles para manejo de
«strings»,además de distintas estrategias de formateado de cadenas.1
7.1.1 Constantes
Las constantes definidas en este módulo son las siguientes:
>>> import string
>>> string.ascii_lowercase
abcdefghijklmnopqrstuvwxyz
>>> string.ascii_uppercase
ABCDEFGHIJKLMNOPQRSTUVWXYZ
>>> string.ascii_letters
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
>>> string.digits
0123456789
>>> string.hexdigits
0123456789abcdefABCDEF
>>> string.octdigits
01234567
>>> string.punctuation
!"#$%&\()*+,-./:;<=>?@[\\]^_{|}~
>>> string.printable
0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$
%&\()*+,-./:;<=>?
˓→@[\\]^_{|}~ \t\n\r\x0b\x0c
>>> string.whitespace
\t\n\r\x0b\x0c
Ejercicio
Dada una cadena de texto, compruebe si todos sus caracteres son dígitos ASCII. Ignore
los espacios en blanco.
Ejemplo
• Entrada: This is it
• Salida: True
Resumen libro de Sergio Delgado Quintero -205-
7.1.2 Plantillas
El módulo string también nos permite usar plantillas con interpolación de variables. Algo
similar a los f-strings pero con otro tipo de sintaxis.
Lo primero es definir la plantilla. Las variables que queramos interporlar deben ir
precedidas
del signo dólar $:
from string import Template
tmpl = Template($lang is the best programming language in the $place!)
Ahora podemos realizar la sustitución con los valores que nos interesen:
>>> tmpl.substitute(lang=Python, place=World)
Python is the best programming language in the World!
(continué en la próxima página)
7.1. string 307 205
Aprende Python
(proviene de la página anterior)
>>> tmpl.substitute({lang: Python, place: World})
Python is the best programming language in the World!
Hay que prestar atención cuando el identificador de variable está seguido por algún
carácter
que, a su vez, puede formar parte del identificador. En este caso hay que utilizar llaves
para
evitar la ambigüedad:
>>> tmpl = Template(You won several ${price}s)
>>> tmpl.substitute(price=phone)
You won several phones
Sustitución segura
En el caso de que alguna de las variables que estamos interpolando no exista o no tenga
ningún valor, obtendremos un error al sustituir:
>>> tmpl = Template($lang is the best programming language in the $place!)
>>> tmpl.substitute(lang=Python)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: place
Para ello Python nos ofrece el método safe_substitute() que no emite error si alguna
variable no es especificada:
>>> tmpl.safe_substitute(lang=Python)
Python is the best programming language in the $place!
Casos de uso
A primera vista podría parecer que este sistema de plantillas no aporta gran ventaja sobre
los f-strings que ya hemos visto. Sin embargo hay ocasiones en los que puede resultar
muy útil.
La mayoría de estas situaciones tienen que ver con la oportunidad de definir el «string».
Si en el momento de crear la plantilla aún no están disponibles las variables de
sustitución, podría interesar utilizar la estrategia que nos proporciona este módulo.
Supongamos un ejemplo en el que tenemos una estructura de «url» y queremos
únicamente sustituir una parte de ella. Para no tener que repetir la cadena de texto
completa en un
«f-string», podríamos seguir este enfoque:
CAPÍTULO 8
Ciencia de datos
La Ciencia de Datos representa uno de los ámbitos de aplicación más importantes dentro
del mundo Python. De hecho, en la encuesta a desarrolladores/as de JetBrains del año
2021 se puede observar que análisis de datos y aprendizaje automático están en 206
primera y cuarta opción en la pregunta ¿para qué utiliza Python?.
Muchos de los paquetes que veremos en esta sección también vienen incluidos por
defecto en Anaconda una plataforma para desarrollo de ciencia de datos que dispone de
instaladores para todos los sistemas operativos.
8.1 jupyter
8.2 numpy
207
8.2.1 ndarray
En el núcleo de NumPy está el ndarray, donde «nd» es por n-dimensional. Un ndarray es
un array multidimensional de elementos del mismo tipo.
Aquí tenemos una diferencia fundamental con las listas en Python que pueden mantener
objetos heterogéneos. Y esta característica propicia que el rendimiento de un ndarray sea
bastante mejor que el de una lista convencional.
Para crear un array podemos usar:
import numpy as np
x = np.array([1, 2, 3, 4, 5])
print(x)
print( type(x))
salida
Resumen libro de Sergio Delgado Quintero -208-
[1 2 3 4 5]
<class 'numpy.ndarray'>
import numpy as np
x = np.array([1, 2, 3, 4, 5])
print(x)
print( type(x)) 208
print(x.ndim) # dimensión
print(x.size) # tamaño total del array
print(x.shape) # forma
print(x.dtype) # tipo de sus elementos
[1 2 3 4 5]
<class 'numpy.ndarray'>
1
5
(5,)
int32
Datos heterogéneos
Hemos dicho que los ndarray son estructuras de datos que almacenan un único tipo de
datos.
A pesar de esto, es posible crear un array con los siguientes valores:
>>> x = np.array([4, Einstein, 1e-7])
Aunque, a priori, puede parecer que estamos mezclando tipos enteros, flotantes y
cadenas de texto, lo que realmente se produce (de forma implícita) es una coerción2 de
tipos a Unicode:
Si creamos un array de números enteros, el tipo de datos por defecto será int64:
import numpy as np
a = np.array(range(10))
print(a)
[0 1 2 3 4 5 6 7 8 9]
Matrices
Una matriz no es más que un array bidimensional. Como ya se ha comentado, NumPy
provee ndarray que se comporta como un array multidimensional con lo que podríamos
crear una matriz sin mayor problema.
Veamos un ejemplo en el que tratamos de construir la siguiente matriz:
Resumen libro de Sergio Delgado Quintero -209-
import numpy as np
M = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]) 209
print(M)
print(M.ndim) # bidimensional
print(M.size)
print(M.shape) # 4 filas x 3 columnas
print(M.dtype)
Valores equiespaciados
A continuación veremos una serie de funciones para crear arrays con valores
equiespaciados o en intervalos definidos.
Valores enteros equiespaciados
La función que usamos para este propósito es np.arange() cuyo comportamiento es
totalmente análogo a la función «built-in» range().
Ejercicio
Resuelva el siguiente sistema de ecuaciones lineales:
3𝑥 + 4𝑦 − 𝑧 = 8
5𝑥 − 2𝑦 + 𝑧 = 4
2𝑥 − 2𝑦 + 𝑧 = 1
8.3 pandas
Resumen libro de Sergio Delgado Quintero -211-
211
pandas es un paquete open-source que nos proporciona una forma sencilla y potente de
trabajar con estructuras de datos a través de múltiples herramientas para su análisis.1
$ pip install pandas
La forma más común de importar esta librería es usar el alias pd:
8.3.1 Series
Podríamos pensar en una serie como un ndarray en el que cada valor tiene asignado una
etiqueta (índice) y además admite un título (nombre).
Creación de una serie
Veamos varios ejemplos de creación de la serie [1, 2, 3].
import pandas as pd
res=pd.Series([1, 2, 3])
print(res)
Nota: El índice por defecto se crea con números enteros positivos empezando desde
0.
import pandas as pd
a 1
b 2
c 3
...
8.4 matplotlib
212
La forma más común de importar esta librería es usar el alias plt de la siguiente manera:
8.4.1 Figura
La figura es el elemento base sobre el que se construyen todos los gráficos en matplotlib.
Veamos cómo crearla:
<class 'matplotlib.figure.Figure'>
Figure(640x480)
x = np.arange(0,10,0.1)
y = x*np.cos(x)
plt.plot(x,y)
plt.xlabel('x')
plt.ylabel('y')
plt.title('Lab DLS')
plt.show()
Resumen libro de Sergio Delgado Quintero -214-
214