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

  1. La filosofía "pilas incluidas"
  2. math: matemáticas más allá de los operadores
  3. random: azar para sorteos y datos de prueba
  4. datetime: fechas y horas de los pedidos
  5. string y textwrap: utilidades de texto
  6. statistics: estadística básica sin instalar nada
  7. sys y os: hablar con el sistema
  8. Mapa de la biblioteca estándar en este curso
  9. Cierre del módulo: menu.py reorganizado

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

Ejemplo 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 25

En 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}")    # 12

Tres 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))
Fausto, sabio insatisfecho, pacta con
Mefistófeles: juventud y conocimiento a
cambio de su alma.

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     mediana

Con 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")
> python consulta.py Hamlet
En catálogo

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 datetime y luego usar datetime.now(): NameError/AttributeError al confundir módulo y clase. O import datetime + datetime.datetime.now(), o from datetime import datetime + datetime.now(). Elige un estilo y sé constante.
  • Esperar que sys.argv traiga números: llega todo como cadenas; convierte con int()/float() como con input().
  • Olvidar que sys.argv[0] es el script: el primer argumento real es sys.argv[1], y hay que comprobar len(sys.argv) antes de usarlo.
  • Nombrar archivos como módulos estándar: un random.py o string.py tuyo eclipsa al oficial (lección 03-04). Los síntomas son errores absurdos en módulos "de toda la vida".
  • Usar random para nada serio en seguridad: para sorteos y pruebas es perfecto; para contraseñas o tokens existe el módulo secrets.
  • 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)}")          # 6

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

Módulo 2: Estructuras de Control

Módulo 3: Funciones y Módulos

Módulo 4: Estructuras de Datos

Módulo 5: Programación Orientada a Objetos

Módulo 6: Manejo de Archivos

Módulo 7: Manejo de Errores y Excepciones

Módulo 8: Temas Avanzados

Módulo 9: Pruebas y Depuración

Módulo 10: Desarrollo Web con Python

Módulo 11: Ciencia de Datos con Python

Módulo 12: Proyecto Final

© Copyright 2026. Todos los derechos reservados