Tabla de Contenidos
Biblioteca estándar y aplicaciones útiles
Biblioteca estándar y aplicaciones útiles: fechas, datos, texto, red y persistencia.
Este módulo forma parte del curso de programación desde cero con Python
El programa que reinventa la rueda
Imagina que necesitas calcular la edad de un usuario a partir de su fecha de nacimiento, validar un correo electrónico, descargar datos de una API o guardar registros en una base de datos. Podrías escribir todo desde cero: una función para parsear fechas, otra para validar correos con condicionales interminables, otra para manejar sockets HTTP… Sería un trabajo enorme, propenso a errores y difícil de mantener. La biblioteca estándar de Python ya resuelve esos problemas, de forma probada y documentada.
Conocer la biblioteca estándar es lo que distingue a un programador que resuelve problemas de uno que reinventa soluciones. Este módulo te presenta los módulos esenciales, con ejemplos prácticos y advertencias de seguridad, para que puedas construir aplicaciones útiles sin depender de decenas de bibliotecas externas.
5. Explicación paso a paso
5.1. `datetime`: fechas y horas
El módulo datetime proporciona clases para manipular fechas y horas. Las principales son date (año, mes, día), time (hora, minuto, segundo, microsegundo) y datetime (fecha y hora combinadas). timedelta representa una duración.
from datetime import datetime, date, timedelta, timezone ahora = datetime.now() hoy = date.today() cumple = date(1990, 5, 20) edad = hoy.year - cumple.year - ((hoy.month, hoy.day) < (cumple.month, cumple.day)) print(ahora) # 2026-10-31 12:34:56.789 print(cumple.strftime("%d/%m/%Y")) # 20/05/1990 print(edad) # 36 # Aritmética con timedelta manana = hoy + timedelta(days=1) print(manana)
Objetos aware y naive: un objeto datetime es aware si incluye información de zona horaria (tzinfo), y naive si no la incluye. Para aplicaciones que cruzan zonas horarias, usa objetos aware con timezone.utc o zoneinfo.ZoneInfo.
ahora_utc = datetime.now(timezone.utc)
[PYTHON]datetimees la referencia para fechas y horas en Python. Para zonas horarias con nombres (por ejemplo,"Europe/Madrid"), usa el módulozoneinfo(Python 3.9+), que implementa la base de datos IANA. La clasetimezonesolo representa desplazamientos fijos respecto a UTC.
[ERROR HABITUAL] Restar dos fechas naive que representan zonas horarias distintas produce resultados incorrectos. Si la aplicación maneja zonas horarias, usa siempre objetos aware.
5.2. `math` y `statistics`: matemáticas y estadísticas
math ofrece funciones matemáticas comunes: sqrt, ceil, floor, log, sin, cos, constantes como pi y e, y funciones de agregación como sumprod o dist.
[NOVEDAD 3.15] Python 3.15 añade el submódulomath.integer(PEP 791), que agrupa funciones matemáticas específicas para enteros, comogcd,lcm,isqrt,combyperm. Estas funciones antes estaban enmath, pero se han movido amath.integerpara separar las operaciones de punto flotante de las enteras. Las versiones antiguas siguen funcionando por compatibilidad, pero el código nuevo debería usarmath.integer.
from math import integer print(integer.gcd(12, 18)) # 6
statistics calcula estadísticas descriptivas: mean, median, mode, stdev, variance, quantiles, etc. A diferencia de math, está pensado para analizar datos, no para funciones matemáticas individuales.
import statistics datos = [10, 20, 30, 40, 50] print(statistics.mean(datos)) # 30 print(statistics.median(datos)) # 30 print(statistics.stdev(datos)) # 15.81...
[PYTHON]statisticsno pretende competir con NumPy o SciPy. Es adecuado para cálculos sencillos en scripts y aplicaciones pequeñas. Para análisis de datos a gran escala, usa bibliotecas especializadas.
[ERROR HABITUAL] Pasar datos conNaNamedian,modeoquantilesproduce resultados inesperados, porqueNaNtiene una semántica de comparación peculiar. Elimina losNaNantes de llamar a estas funciones.
random y secrets: aleatoriedad
random genera números pseudoaleatorios para simulaciones, juegos y muestreo. No es criptográficamente seguro.
import random print(random.randint(1, 10)) # Entero entre 1 y 10 print(random.choice(["a", "b", "c"])) # Elección aleatoria random.shuffle(lista) # Mezcla in situ
secrets genera números y tokens criptográficamente seguros. Úsalo para contraseñas, tokens de sesión, URLs de restablecimiento y cualquier valor que deba ser impredecible.
import secrets token = secrets.token_urlsafe(32) contrasena = secrets.token_hex(16)
[SEGURIDAD] No usesrandompara generar contraseñas, tokens o claves.randomes determinista si se conoce la semilla, y un atacante podría predecir los valores. La documentación oficial de Python es explícita: “randomshouldn’t be used for security purposes, usesecretsinstead”.
5.4. `argparse`: interfaces de línea de comandos
argparse permite definir argumentos posicionales y opcionales, generar ayuda automática y validar tipos.
import argparse parser = argparse.ArgumentParser(description="Saluda a una persona.") parser.add_argument("nombre", help="Nombre de la persona") parser.add_argument("--veces", type=int, default=1, help="Número de saludos") args = parser.parse_args() for _ in range(args.veces): print(f"Hola, {args.nombre}")
[PYTHON]argparsees el módulo recomendado por la documentación oficial para interfaces de línea de comandos en la biblioteca estándar. Para interfaces más complejas,clickotyperson alternativas populares de terceros.
5.5. `os.environ`: variables de entorno
Las variables de entorno son pares clave-valor que el sistema operativo pasa a los procesos. Se usan para configuración (rutas, credenciales, modos de ejecución) sin modificar el código. os.environ es un diccionario que las representa.
import os # Leer una variable obligatoria (lanza KeyError si no existe) modo = os.environ["MODO"] # Leer una variable opcional con valor por defecto debug = os.getenv("DEBUG", "false").lower() == "true"
[BUENA PRÁCTICA] No hardcodees credenciales en el código. Usa variables de entorno o archivos.env(conpython-dotenv) para configuración sensible. Y añade.enval.gitignore(Módulo 19).
5.6. `pathlib`: archivos y directorios (repaso)
pathlib (Módulo 13) es la forma moderna y portable de manipular rutas. Ofrece operaciones como exists(), is_file(), mkdir(), iterdir(), read_text() y write_text().
from pathlib import Path ruta = Path.home() / "datos" / "registro.txt" ruta.parent.mkdir(parents=True, exist_ok=True) ruta.write_text("Hola\n", encoding="utf-8")
[PYTHON] En Python 3.15,PurePath.is_reserved()se elimina (deprecado desde 3.13). Usaos.path.isreserved()si necesitas comprobar nombres reservados en Windows.
5.7. `re`: expresiones regulares
Las expresiones regulares son patrones para buscar, extraer y sustituir texto. El módulo re ofrece funciones como search, findall, sub y compile.
import re texto = "Contacto: ana@example.com, luis@example.org" correos = re.findall(r"[\w.+-]+@[\w-]+\.[\w.]+", texto) print(correos) # ['ana@example.com', 'luis@example.org'] # Sustituir anonimizado = re.sub(r"[\w.+-]+@[\w-]+\.[\w.]+", "[email]", texto)
[NOVEDAD 3.15]re.match()está soft-deprecado en Python 3.15. Se ha añadidore.prefixmatch()como nombre más claro, porquematch()ancla al principio de la cadena pero no al final, lo que confunde a muchos principiantes. En la mayoría de los casos, querrásre.search()(busca en cualquier posición) ore.fullmatch()(coincidencia completa).
[BUENA PRÁCTICA] Usa raw strings (r"...")para los patrones. Sinr, las barras invertidas se interpretan como secuencias de escape de Python, lo que produce errores difíciles de diagnosticar.
5.8. `urllib.request`: HTTP y APIs
urllib.request abre URLs y permite hacer peticiones HTTP básicas. Para APIs JSON, el flujo típico es: abrir la URL, leer la respuesta y parsearla con json.
import json import urllib.request with urllib.request.urlopen("https://api.example.com/datos", timeout=10) as respuesta: datos = json.load(respuesta) print(datos["resultado"])
[PYTHON]urllib.requestes funcional pero de bajo nivel. Para un cliente HTTP más cómodo, la comunidad recomienda la biblioteca de tercerosrequests, que simplifica el manejo de sesiones, JSON y errores. La documentación oficial deurllib.requestla menciona como alternativa recomendada.
[SEGURIDAD] Nunca desactives la verificación de certificados TLS (ssl). Si una petición falla por un error de certificado, no es un problema que debas “saltarte”: indica que la conexión no es segura.
5.9. `sqlite3`: bases de datos ligeras
SQLite es una base de datos relacional en un solo archivo, sin servidor. El módulo sqlite3 implementa la especificación DB-API 2.0 (PEP 249).
import sqlite3 con = sqlite3.connect("tutorial.db") cur = con.cursor() cur.execute("CREATE TABLE IF NOT EXISTS peliculas (titulo TEXT, anio INTEGER)") cur.execute("INSERT INTO peliculas VALUES (?, ?)", ("Monty Python", 1975)) con.commit() for fila in cur.execute("SELECT titulo, anio FROM peliculas"): print(fila) con.close()
[PYTHON] Usa siempre parámetros (?) en lugar de concatenar cadenas para evitar inyección SQL.cur.execute("SELECT * FROM t WHERE id = ?", (id,))es seguro;cur.execute(f"SELECT * FROM t WHERE id = {id}")es vulnerable.
[NOVEDAD 3.15] En Python 3.15, los parámetros desqlite3.connect()y del constructorConnectionpasan a ser solo por palabra clave (keyword-only), y algunas APIs deConnectionpasan a ser solo posicionales (positional-only). Si tienes código antiguo que pasa argumentos por posición, revisa la documentación al migrar.
5.10. Seguridad básica
La biblioteca estándar incluye módulos con consideraciones de seguridad específicas. Las más importantes para el nivel de este curso:
- random no es seguro para secretos. Usa secrets.
- pickle y shelve no son seguros con datos no confiables. Pueden ejecutar código arbitrario al deserializar.
- eval() y exec() ejecutan código arbitrario. Evítalos con datos de entrada.
- No desactives la verificación TLS.
- Usa el modo aislado (-I) o -P si ejecutas scripts en entornos no confiables, para evitar que el directorio actual se añada a sys.path.
[BUENA PRÁCTICA] La seguridad no es una característica que se añade al final: es una propiedad del diseño. Valida las entradas, usa parámetros en SQL, no confíes en datos externos y mantén las dependencias actualizadas.
6. Ejemplo completo comentado: descargar, procesar y guardar datos
import json import sqlite3 import statistics import urllib.request from pathlib import Path def descargar_datos(url): """Descarga datos JSON de una URL y devuelve una lista.""" with urllib.request.urlopen(url, timeout=10) as respuesta: return json.load(respuesta) def guardar_en_sqlite(datos, ruta_db): """Guarda una lista de diccionarios en una tabla SQLite.""" con = sqlite3.connect(ruta_db) cur = con.cursor() cur.execute("CREATE TABLE IF NOT EXISTS registros (valor REAL)") cur.executemany("INSERT INTO registros VALUES (?)", [(d["valor"],) for d in datos]) con.commit() con.close() def calcular_estadisticas(valores): """Devuelve media, mediana y desviación típica.""" return { "media": statistics.mean(valores), "mediana": statistics.median(valores), "stdev": statistics.stdev(valores) if len(valores) > 1 else 0, } # Uso # datos = descargar_datos("https://api.example.com/datos") # valores = [d["valor"] for d in datos] # print(calcular_estadisticas(valores)) # guardar_en_sqlite(datos, Path("datos.db"))
Seguimiento: El ejemplo combina varios módulos: urllib.request para la red, json para parsear, statistics para el análisis y sqlite3 para la persistencia. Cada función tiene una responsabilidad clara (Módulo 9 y 16). Los errores de red o de base de datos deben manejarse con try/except (Módulo 11) en código de producción.
7. Errores habituales
| Error | Causa | Cómo evitarlo |
|---|---|---|
KeyError en os.environ["VAR"] | La variable no existe. | Usa os.getenv("VAR", default) si es opcional. |
re.match() no encuentra lo esperado | match solo busca al principio. | Usa re.search() o re.fullmatch(); en 3.15, re.prefixmatch(). |
| Inyección SQL | Concatenar cadenas en execute. | Usa parámetros ?. |
| Contraseñas predecibles | Usar random. | Usa secrets. |
datetime naive en zonas horarias | No se especifica tzinfo. | Usa timezone.utc o zoneinfo.ZoneInfo. |
NaN en statistics.median | Los NaN rompen el orden. | Filtra los NaN antes. |
urllib falla por certificado | TLS no verificado o cadena incompleta. | No desactives la verificación; revisa la configuración de certificados. |
8. Particularidades de Python frente a otros lenguajes
- Biblioteca estándar “batteries included”: Python incluye módulos para HTTP, SQLite, JSON, CSV, expresiones regulares, correo, compresión, etc. En C o Java, muchas de estas funcionalidades requieren bibliotecas externas.
- argparse en la biblioteca estándar: Java o C no tienen un equivalente directo; suelen usar bibliotecas de terceros.
- secrets frente a random: la separación explícita entre aleatoriedad estadística y criptográfica es una práctica de seguridad que Python hace visible en la biblioteca estándar.
- sqlite3 integrado: SQLite es una biblioteca C externa, pero Python la incluye como módulo estándar, lo que facilita prototipos y aplicaciones pequeñas.
- re.prefixmatch() (3.15): la evolución del lenguaje para corregir nombres confusos (match) muestra la importancia de la legibilidad en la API estándar.
9. Buenas prácticas
- Consulta la documentación oficial antes de escribir una función desde cero. La biblioteca estándar suele tener una solución probada.
- Usa pathlib para rutas portables.
- Usa secrets para cualquier valor que deba ser impredecible.
- Usa parámetros en SQL para evitar inyección.
- Maneja los errores de red y de base de datos con try/except específicos.
- No hardcodees configuración sensible: usa variables de entorno.
- Para HTTP de alto nivel, considera requests en lugar de urllib.request.
10. Windows y Linux
- datetime, statistics, math, re, sqlite3, urllib funcionan igual en ambos sistemas.
- os.environ es portable, pero las variables de entorno disponibles dependen del sistema. Documenta las que uses.
- pathlib unifica las rutas; evita concatenar con \ o / manualmente.
- sqlite3 crea el archivo de base de datos en la ruta indicada; usa pathlib para construirla de forma portable.
- urllib.request usa los certificados del sistema; en algunos entornos Linux minimalistas puede ser necesario instalar ca-certificates.
11. Ejercicios guiados
Ejercicio guiado 1: Calcular la edad.
Pide al usuario su fecha de nacimiento en formato DD/MM/AAAA y calcula su edad con datetime. Maneja el caso de formato inválido con try/except ValueError.
Ejercicio guiado 2: Estadísticas de una lista.
Pide una lista de números separados por comas, conviértelos a float y muestra la media, la mediana y la desviación típica con statistics.
Ejercicio guiado 3: Token seguro.
Genera un token de restablecimiento de contraseña con secrets.token_urlsafe(32). Muestra el token y explica por qué no usarías random para esto.
12. Ejercicios autónomos
1. Expresiones regulares: Extrae todas las direcciones de correo de un texto y sustitúyelas por [oculto].
2. argparse: Escribe un script que reciba una ruta de archivo y una palabra, y cuente cuántas veces aparece la palabra en el archivo. Usa argparse con ayuda automática.
3. SQLite: Crea una base de datos agenda.db con una tabla contactos(nombre, telefono). Inserta tres contactos y consulta todos los que empiecen por “A”.
4. HTTP: Descarga un JSON de una API pública (por ejemplo, https://api.github.com/users/python) y muestra el nombre y el número de repositorios públicos.
5. Reto opcional: Investiga math.integer en Python 3.15 y escribe un script que calcule el máximo común divisor y el mínimo común múltiplo de una lista de números usando este submódulo.
13. Soluciones razonadas
Solución al ejercicio guiado 1:
from datetime import datetime fecha_texto = input("Fecha de nacimiento (DD/MM/AAAA): ") try: nacimiento = datetime.strptime(fecha_texto, "%d/%m/%Y").date() hoy = datetime.now().date() edad = hoy.year - nacimiento.year - ((hoy.month, hoy.day) < (nacimiento.month, nacimiento.day)) print(f"Tienes {edad} años.") except ValueError: print("Formato de fecha inválido.")
strptime parsea la fecha; el cálculo de edad ajusta el año si aún no ha llegado el cumpleaños.
Solución al ejercicio 2:
import statistics entrada = input("Números separados por comas: ") numeros = [float(n) for n in entrada.split(",")] print(statistics.mean(numeros)) print(statistics.median(numeros)) print(statistics.stdev(numeros))
Solución al ejercicio 3: secrets.token_urlsafe(32) genera un token seguro. No se usa random porque su secuencia es predecible si se conoce la semilla, lo que comprometería la seguridad del restablecimiento.
Solución al ejercicio 5 (orientativa):
from math import integer numeros = [12, 18, 24] print(integer.gcd(*numeros)) print(integer.lcm(*numeros))
math.integer agrupa las funciones enteras en 3.15.
Resumen
- La biblioteca estándar cubre fechas, estadísticas, aleatoriedad, CLI, entorno, archivos, texto, red y bases de datos.
datetimemaneja fechas y horas; usa objetos aware para zonas horarias.mathystatisticsofrecen funciones matemáticas y estadísticas;math.integeres nuevo en 3.15.randomes para simulaciones;secretspara seguridad.argparsecrea interfaces de línea de comandos con ayuda automática.os.environypathlibgestionan configuración y rutas.reusa expresiones regulares;re.prefixmatch()sustituye are.match()en 3.15.urllib.requesthace peticiones HTTP;requestses la alternativa de alto nivel.sqlite3integra SQLite; usa parámetros para evitar inyección SQL.- La seguridad básica incluye no usar
randompara secretos, no confiar enpicklecon datos externos y no desactivar TLS.
