En la lección anterior aprendiste a importar módulos y creaste el tuyo propio. Ahora toca abrir el regalo que viene con cada instalación de Python: la biblioteca estándar, más de doscientos módulos ya escritos, probados y documentados que cubren matemáticas, azar, fechas, texto, archivos y mucho más — sin instalar nada, sin pip, sin .venv que tocar. Los pythonistas la llaman la filosofía "pilas incluidas" (batteries included): antes de programar algo, pregunta si la biblioteca estándar ya lo trae. Esta lección es una visita guiada, no una enciclopedia: verás los módulos que más usarás con ejemplos breves aplicados a Papyrus, y un mapa de cuáles retomaremos a fondo en el resto del curso. Cerraremos el módulo con la versión definitiva (por ahora) de menu.py, reescrita sobre papyrus_utils.
Contenido
- La filosofía "pilas incluidas"
math: matemáticas más allá de los operadoresrandom: azar para sorteos y datos de pruebadatetime: fechas y horas de los pedidosstringytextwrap: utilidades de textostatistics: estadística básica sin instalar nadasysyos: hablar con el sistema- Mapa de la biblioteca estándar en este curso
- Cierre del módulo:
menu.pyreorganizado
La filosofía "pilas incluidas"
Todo lo que viste en 03-04 aplica tal cual: import math funciona igual que import papyrus_utils, solo que el archivo vive en las carpetas de la biblioteca estándar de sys.path en lugar de en tu proyecto. La documentación oficial (docs.python.org, sección Library Reference) es la referencia canónica; help(modulo) en el REPL es el atajo rápido. La habilidad a entrenar no es memorizar funciones, sino saber que el módulo existe: quien sabe que statistics existe no reimplementa la media con un bucle.
math: matemáticas más allá de los operadores
Los operadores (+, **, //...) cubren lo básico; math aporta el resto:
import math
print(math.sqrt(16)) # 4.0 raíz cuadrada
print(math.ceil(2.04)) # 3 redondeo SIEMPRE hacia arriba
print(math.floor(9.83)) # 9 redondeo SIEMPRE hacia abajo
print(math.pi) # 3.141592653589793Ejemplo de tienda: los libros llegan en cajas de 6 unidades. Si Ana necesita 20 ejemplares, ¿cuántas cajas pide?
cajas = math.ceil(20 / 6)
print(f"Pedido: {cajas} cajas") # 4 (con 3.33 cajas no basta: se redondea hacia arriba)Ojo al matiz frente a round(), que ya conoces: round(3.33) da 3 (al más cercano), math.ceil(3.33) da 4 (techo). Para "¿cuántos contenedores necesito?", siempre ceil.
random: azar para sorteos y datos de prueba
import random
print(random.randint(1, 6)) # entero entre 1 y 6, AMBOS incluidos
print(random.choice(catalogo)) # un elemento al azar de la lista
print(random.sample(catalogo, 2)) # 2 elementos distintos, sin repetir
print(round(random.uniform(5, 25), 2)) # float al azar entre 5 y 25En Papyrus, random.choice() resuelve la "recomendación sorpresa del día", y random.sample() un sorteo entre socios sin repetir ganador. Su otro gran uso profesional: generar datos de prueba (precios, stocks) para ensayar programas antes de tener datos reales. Y un truco de laboratorio: random.seed(42) fija la semilla del generador para que la secuencia "aleatoria" sea reproducible — esencial para depurar y, más adelante, para las pruebas del módulo 9.
datetime: fechas y horas de los pedidos
El módulo datetime contiene (entre otras) las clases date y datetime — sí, módulo y clase se llaman igual, origen de confusiones eternas. Lo habitual es importar las clases directamente:
from datetime import date, datetime, timedelta
hoy = date.today()
ahora = datetime.now()
print(hoy) # 2026-07-13
print(f"{ahora:%d/%m/%Y %H:%M}") # 13/07/2026 10:45 (formato a medida)
# El pedido de "Fausto" llega en 7 días:
llegada = hoy + timedelta(days=7)
print(f"El pedido de Fausto llegará el {llegada:%d/%m/%Y}")
# ¿Cuántos días lleva esperando Luis? (restar fechas da un timedelta)
encargo = date(2026, 7, 1)
print(f"Días de espera: {(hoy - encargo).days}") # 12Tres ideas clave: los objetos fecha se operan (sumar un timedelta, restar dos fechas), se formatean en f-strings con códigos %d, %m, %Y, %H, %M, y se construyen con date(año, mes, dia). Con esto Papyrus puede fechar tickets y calcular entregas; cuando la tienda guarde pedidos en archivos (módulo 6), las fechas viajarán como texto y datetime las interpretará de vuelta.
string y textwrap: utilidades de texto
Dos módulos pequeños pero resultones. string ofrece constantes listas para validar:
import string
print(string.ascii_lowercase) # abcdefghijklmnopqrstuvwxyz
print(string.digits) # 0123456789
print(string.punctuation) # !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~Por ejemplo, para comprobar que un código de descuento solo usa letras mayúsculas y dígitos: all(c in string.ascii_uppercase + string.digits for c in codigo).
textwrap ajusta párrafos a un ancho dado — perfecto para que la sinopsis de un libro quepa en el ticket de 40 columnas de Papyrus:
import textwrap
sinopsis = ("Fausto, sabio insatisfecho, pacta con Mefistófeles: "
"juventud y conocimiento a cambio de su alma.")
print(textwrap.fill(sinopsis, width=40))statistics: estadística básica sin instalar nada
Antes de llegar a pandas (módulo 11), la estadística de andar por casa ya viene incluida:
import statistics
precios = [12.50, 9.95, 15.90, 21.00]
print(statistics.mean(precios)) # 14.8375 media
print(statistics.median(precios)) # 14.2 medianaCon mean() Ana responde "¿cuál es el precio medio de mi catálogo?" en una línea. La mediana resiste mejor los valores extremos: si entrara un facsímil de 300 €, la media se dispararía y la mediana apenas se movería.
sys y os: hablar con el sistema
De sys ya conoces sys.path (lección 03-04). Su otra estrella es sys.argv: la lista de argumentos de línea de comandos con los que se ejecutó el script — el equivalente para scripts de los parámetros de una función. sys.argv[0] es el nombre del propio script y el resto, lo que se escribió detrás:
# consulta.py — uso: python consulta.py <titulo>
import sys
from papyrus_utils import buscar_libro
catalogo = ["La Odisea", "Hamlet", "El Quijote"]
if len(sys.argv) < 2:
print("Uso: python consulta.py <titulo>")
else:
indice = buscar_libro(catalogo, sys.argv[1])
print("En catálogo" if indice is not None else "No disponible")Así Ana consulta sin pasar por el menú: scripts "de un golpe" para tareas rápidas. Ojo: los argumentos llegan siempre como cadenas, igual que con input().
Por su parte, os (y su moderno compañero pathlib) comunica con el sistema operativo: os.getcwd() dice en qué carpeta estás, os.listdir() lista archivos, y de ahí en adelante — crear carpetas, comprobar si un archivo existe, borrar. Solo te lo presento: es protagonista del módulo 6, cuando el catálogo viva en archivos.
Mapa de la biblioteca estándar en este curso
| Módulo | Para qué sirve | Dónde se usa en este curso |
|---|---|---|
math |
Raíces, techos/suelos, constantes | Desde ya, donde haga falta |
random |
Azar, muestreo, datos de prueba | Aquí; datos de ejemplo en módulos 4-6 |
datetime |
Fechas, horas, aritmética temporal | Aquí; fechas de pedidos en módulo 6 (CSV/JSON) |
string / textwrap |
Constantes de texto / párrafos a medida | Curiosidades útiles; cadenas a fondo en 04-05 |
statistics |
Media, mediana y compañía | Aquí; antesala de pandas (módulo 11) |
sys |
argv, path, salida del intérprete |
Aquí y en depuración (módulo 9) |
os / pathlib |
Archivos, carpetas, rutas | Módulo 6 |
collections |
Estructuras de datos extra | Módulo 4 (04-06) |
csv / json |
Leer y escribir datos estructurados | Módulo 6 |
logging |
Registro profesional de eventos | Módulo 7 |
unittest / pdb |
Pruebas y depuración | Módulo 9 |
asyncio / threading |
Concurrencia | Módulo 8 |
Guarda esta tabla como brújula: gran parte del temario que te queda es, en el fondo, aprender a usar bien la biblioteca estándar.
Cierre del módulo: menu.py reorganizado
La promesa del módulo 2 queda saldada. La versión final de menu.py importa papyrus_utils (lección 03-04), reparte el trabajo en funciones con una responsabilidad cada una (03-01), usa argumentos keyword (03-02) y hasta fecha la sesión con datetime:
"""Menú de gestión de la librería Papyrus."""
from datetime import date
from papyrus_utils import NOMBRE_TIENDA, buscar_libro, precio_final
catalogo = ["La Odisea", "Hamlet", "El Quijote"]
precios = [12.50, 9.95, 15.90]
stocks = [4, 6, 8]
def mostrar_catalogo():
"""Imprime la tabla de títulos, precios y stock."""
print(f"\n{'Título':<12} {'Precio':>8} {'Stock':>6}")
print("-" * 28)
for titulo, precio, stock in zip(catalogo, precios, stocks):
print(f"{titulo:<12} {precio:>6.2f} € {stock:>6}")
def consultar_libro():
"""Pide un título y muestra su ficha, con precio de socio incluido."""
buscado = input("Título a buscar: ")
if not buscado:
print("No has escrito nada.")
return
indice = buscar_libro(catalogo, buscado)
if indice is None:
print(f"'{buscado}' no está en el catálogo.")
return
base = precios[indice]
print(f"'{catalogo[indice]}': {base:.2f} € "
f"(socios: {precio_final(base, socio=True):.2f} €), stock {stocks[indice]}.")
def ejecutar_menu():
"""Bucle principal del menú de la tienda."""
print(f"Gestión de {NOMBRE_TIENDA} — {date.today():%d/%m/%Y}")
while True:
print("\n1. Ver catálogo\n2. Buscar un libro\n3. Salir")
match input("Elige una opción (1-3): "):
case "1":
mostrar_catalogo()
case "2":
consultar_libro()
case "3":
print(f"Cerrando {NOMBRE_TIENDA}. ¡Hasta mañana, Ana!")
break
case _:
print("Opción no válida. Elige 1, 2 o 3.")
if __name__ == "__main__":
ejecutar_menu()Compáralo con la versión monolítica del módulo 2: hace lo mismo y algo más (fecha, precio de socio en la ficha), pero ahora cada pieza tiene nombre, docstring y sitio. consultar_libro() usa return como guarda — sus salidas tempranas sustituyen al continue de la versión antigua. La lógica de negocio vive en papyrus_utils (reutilizable desde ticket.py o venta_socio.py), la presentación vive aquí, y el bloque __main__ permite que otro script haga from menu import mostrar_catalogo sin arrancar el menú entero. Esto ya no es un script: es un programa pequeño bien organizado.
Errores Comunes y Consejos
- Reinventar la rueda: escribir tu propia media, tu propio barajado o tu propio ajuste de líneas. Antes de programar una utilidad "genérica", busca en la documentación: probablemente ya existe, sin errores y más rápida.
import datetimey luego usardatetime.now():NameError/AttributeErroral confundir módulo y clase. Oimport datetime+datetime.datetime.now(), ofrom datetime import datetime+datetime.now(). Elige un estilo y sé constante.- Esperar que
sys.argvtraiga números: llega todo como cadenas; convierte conint()/float()como coninput(). - Olvidar que
sys.argv[0]es el script: el primer argumento real essys.argv[1], y hay que comprobarlen(sys.argv)antes de usarlo. - Nombrar archivos como módulos estándar: un
random.pyostring.pytuyo eclipsa al oficial (lección 03-04). Los síntomas son errores absurdos en módulos "de toda la vida". - Usar
randompara nada serio en seguridad: para sorteos y pruebas es perfecto; para contraseñas o tokens existe el módulosecrets. - Consejo: dedica cinco minutos a hojear el índice de la Library Reference oficial. No para aprenderlo, sino para que "me suena que hay un módulo para esto" se convierta en tu primer reflejo.
Ejercicios
Ejercicio 1: sorteo del club de lectura
Escribe sorteo.py: dada socios = ["Luis", "Marta", "Chen", "Aitana", "Pau"], elige con random.sample() dos ganadores distintos de un ejemplar de "Fausto" e imprime cada uno con la fecha de recogida límite: hoy más 15 días, formateada dd/mm/aaaa (usa date.today() y timedelta).
Ejercicio 2: informe express del catálogo
Escribe informe.py que, con precios = [12.50, 9.95, 15.90, 21.00], imprima: el precio medio y la mediana (módulo statistics, ambos con :.2f), el precio más alto y el más bajo (funciones integradas max()/min()), y cuántas cajas de 6 unidades necesita Ana para guardar 31 libros (módulo math). Remata con una línea separadora de guiones generada con "-" * 30.
Ejercicio 3: consulta con argumentos y papyrus_utils
Amplía el consulta.py de esta lección: debe aceptar python consulta.py <titulo> [socio]. Si el título existe en el catálogo, imprime su precio final — de socio si el segundo argumento es exactamente socio, normal en caso contrario — usando precio_final() de papyrus_utils. Si no existe o falta el título, imprime el aviso correspondiente. Pruébalo con python consulta.py Hamlet socio (debe dar 9.83 €).
Soluciones
Ejercicio 1:
import random
from datetime import date, timedelta
socios = ["Luis", "Marta", "Chen", "Aitana", "Pau"]
ganadores = random.sample(socios, 2)
limite = date.today() + timedelta(days=15)
for nombre in ganadores:
print(f"{nombre}: recoge tu 'Fausto' antes del {limite:%d/%m/%Y}")sample() garantiza dos nombres distintos (con choice() dos veces, Luis podría ganar ambos ejemplares). La fecha se calcula una vez y se reutiliza.
Ejercicio 2:
import math
import statistics
precios = [12.50, 9.95, 15.90, 21.00]
print(f"Precio medio: {statistics.mean(precios):.2f} €") # 14.84 €
print(f"Mediana: {statistics.median(precios):.2f} €") # 14.20 €
print(f"Más caro: {max(precios):.2f} €") # 21.00 €
print(f"Más barato: {min(precios):.2f} €") # 9.95 €
print("-" * 30)
print(f"Cajas para 31 libros: {math.ceil(31 / 6)}") # 6Fíjate en la mezcla natural de funciones integradas (max, min) y módulos (statistics, math): la biblioteca estándar empieza donde las integradas terminan.
Ejercicio 3:
import sys
from papyrus_utils import buscar_libro, precio_final
catalogo = ["La Odisea", "Hamlet", "El Quijote"]
precios = [12.50, 9.95, 15.90]
if len(sys.argv) < 2:
print("Uso: python consulta.py <titulo> [socio]")
else:
indice = buscar_libro(catalogo, sys.argv[1])
if indice is None:
print(f"'{sys.argv[1]}' no está en el catálogo.")
else:
es_socio = len(sys.argv) > 2 and sys.argv[2] == "socio"
print(f"{catalogo[indice]}: {precio_final(precios[indice], socio=es_socio):.2f} €")es_socio aprovecha el cortocircuito del and: si no hay tercer argumento, no intenta leer sys.argv[2] (evitando un IndexError). Nota los imports en dos bloques PEP 8: estándar (sys) arriba, propios (papyrus_utils) debajo.
Conclusión
El módulo 3 cierra el círculo que abrió: el código de Papyrus ya no vive suelto. Sabes definir funciones con def, docstring y return (03-01); diseñar sus firmas con argumentos posicionales, keyword, valores por defecto, *args/**kwargs y recorrer listas paralelas con zip() (03-02); escribir lambdas de usar y tirar como criterio de sorted(), min() y max() (03-03); repartir el código en módulos propios como papyrus_utils.py con if __name__ == "__main__" y paquetes con __init__.py (03-04); y orientarte por la biblioteca estándar con math, random, datetime, statistics o sys.argv (03-05). El nuevo menu.py — funciones pequeñas, negocio importado, presentación separada — es la prueba de madurez. Pero queda una grieta a la vista: el catálogo son tres listas paralelas que hay que mantener sincronizadas a mano, y ya nos ha obligado a malabares con zip() e índices. El módulo 4 ataca justo ahí: las estructuras de datos de Python — listas a fondo, tuplas, diccionarios, conjuntos y las joyas de collections — con las que cada libro de Papyrus será por fin una sola pieza de información, y no un número de fila repartido en tres listas.
Curso de Programación en Python
Módulo 1: Introducción a Python
- Introducción a Python
- Configuración del Entorno de Desarrollo
- Sintaxis de Python y Tipos de Datos Básicos
- Variables y Constantes
- Entrada y Salida Básica
- Entornos Virtuales y Gestión de Paquetes
Módulo 2: Estructuras de Control
- Sentencias Condicionales
- Bucles: for y while
- Herramientas de Control de Flujo
- Comprensiones de Listas
Módulo 3: Funciones y Módulos
- Definición de Funciones
- Argumentos de Función
- Funciones Lambda
- Módulos y Paquetes
- Visión General de la Biblioteca Estándar
Módulo 4: Estructuras de Datos
Módulo 5: Programación Orientada a Objetos
Módulo 6: Manejo de Archivos
- Lectura y Escritura de Archivos
- Trabajo con Archivos CSV
- Manejo de Datos JSON
- Operaciones con Archivos y Directorios
Módulo 7: Manejo de Errores y Excepciones
- Introducción a las Excepciones
- Manejo de Excepciones
- Lanzamiento de Excepciones
- Excepciones Personalizadas
- Buenas Prácticas y Registro de Errores con logging
Módulo 8: Temas Avanzados
- Anotaciones de Tipos (type hints)
- Decoradores
- Generadores
- Administradores de Contexto
- Concurrencia: Hilos y Procesos
- Asyncio para Programación Asíncrona
Módulo 9: Pruebas y Depuración
- Introducción a las Pruebas
- Pruebas Unitarias con unittest
- Pruebas con pytest
- Desarrollo Guiado por Pruebas
- Técnicas de Depuración
- Uso de pdb para Depuración
Módulo 10: Desarrollo Web con Python
- Introducción al Desarrollo Web
- Fundamentos del Framework Flask
- Construcción de APIs REST con Flask
- Introducción a Django
- Construcción de Aplicaciones Web con Django
Módulo 11: Ciencia de Datos con Python
- Introducción a la Ciencia de Datos
- NumPy para Computación Numérica
- Pandas para Manipulación de Datos
- Matplotlib para Visualización de Datos
- Introducción al Aprendizaje Automático con scikit-learn
