Saltar al contenido
biblioteca-estandar.py · devschool

La biblioteca estándar de Python

Lección 19 de 31 · 11 min de lectura · Actualizado el

En esta lección
  1. Cómo orientarte
  2. math: algo más que raíces
  3. random: azar que puedes repetir
  4. statistics: resumir datos
  5. pathlib y os: archivos y carpetas
  6. sys: el intérprete y la línea de comandos
  7. collections: estructuras de datos extra
  8. itertools: combinar y agrupar
  9. time: medir y esperar
  10. shutil: copiar, mover y borrar en bloque
  11. Errores frecuentes
  12. Resumen

Python trae instalados de serie cientos de módulos que resuelven tareas muy comunes: cálculos, números aleatorios, estadística, rutas y carpetas, conteos, combinaciones o medir tiempos. A ese conjunto se le llama biblioteca estándar. En la lección de módulos viste cómo importarlos y un vistazo rápido a cada uno; aquí vas a ir un paso más allá, con un ejemplo útil de cada módulo para que sepas cuándo echar mano de él antes de reinventar la rueda.

Cómo orientarte

Antes de escribir una función, pregúntate si ya existe. La documentación oficial (docs.python.org, con versión en español) tiene un índice de toda la biblioteca. Desde la consola interactiva también puedes explorar:

import statistics

print([n for n in dir(statistics) if n.startswith("med")])
# ['median', 'median_grouped', 'median_high', 'median_low']

dir() lista lo que contiene un módulo (aquí, solo los nombres que empiezan por “med”) y help(statistics.median) muestra la ayuda de una función concreta.

math: algo más que raíces

Además de sqrt, ceil y floor, math tiene funciones que resuelven problemas muy concretos.

import math

# ¿Cuántas cajas de 12 huevos necesito para 50 huevos?
print(math.ceil(50 / 12))              # 5

# Comparar decimales sin sorpresas
print(0.1 + 0.2 == 0.3)                # False
print(math.isclose(0.1 + 0.2, 0.3))    # True

# Distancia en línea recta entre dos puntos (x, y)
print(math.dist((0, 0), (3, 4)))       # 5.0

# Producto de todos los elementos y máximo común divisor
print(math.prod([2, 3, 4]))            # 24
print(math.gcd(48, 36))                # 12
  • math.isclose es la forma correcta de comparar números con decimales. Por cómo se guardan en memoria, 0.1 + 0.2 vale 0.30000000000000004, y == falla.
  • math.gcd (máximo común divisor) sirve, por ejemplo, para simplificar fracciones o repartir en grupos iguales.
  • math.inf representa el infinito; es útil como valor inicial al buscar un mínimo.

random: azar que puedes repetir

random genera números pseudoaleatorios: parecen al azar, pero salen de una fórmula que parte de un valor inicial llamado semilla. Si fijas la semilla con random.seed, obtienes siempre la misma secuencia. Es muy útil para hacer pruebas o para que un compañero pueda reproducir tu resultado.

import random

random.seed(42)
print(random.randint(1, 6))                         # 6
print(random.choice(["Ana", "Luis", "Eva"]))        # Ana
print(round(random.uniform(10, 20), 2))             # 10.25

Ejecútalo varias veces y verás siempre lo mismo. Quita la línea de seed y cada ejecución dará algo distinto.

Un sorteo de grupos reproducible

alumnos = ["Ana", "Luis", "Eva", "Marcos", "Sara", "Iker"]

random.seed(2026)
mezclados = alumnos[:]          # copia, para no desordenar la original
random.shuffle(mezclados)
grupos = [mezclados[i:i + 2] for i in range(0, len(mezclados), 2)]
print(grupos)
# [['Sara', 'Luis'], ['Marcos', 'Iker'], ['Eva', 'Ana']]

Elegir con pesos: choices

choice elige un elemento; choices elige varios con reemplazo (pueden repetirse) y admite pesos:

random.seed(8)
premios = ["nada", "pegatina", "camiseta"]
print(random.choices(premios, weights=[70, 25, 5], k=6))
# ['nada', 'camiseta', 'nada', 'pegatina', 'nada', 'nada']

Cada tirada tiene un 70 % de “nada”, un 25 % de “pegatina” y un 5 % de “camiseta”. Si no quieres repeticiones, usa random.sample(lista, k).

Cuidado: random no es seguro para contraseñas, tokens o códigos de verificación, porque su secuencia se puede predecir. Para eso usa el módulo secrets: secrets.token_hex(16) o secrets.choice(caracteres).

statistics: resumir datos

Imagina que registras los minutos que tardas en una ruta de running cada día:

import statistics as st

tiempos = [31, 29, 35, 30, 29, 42, 28]

print(st.mean(tiempos))              # 32 (media)
print(st.median(tiempos))            # 30 (mediana)
print(st.mode(tiempos))              # 29 (el más repetido)
print(round(st.stdev(tiempos), 2))   # 4.97 (desviación típica)
print(st.quantiles(tiempos, n=4))    # [29.0, 30.0, 35.0]

La media se ve afectada por el día malo de 42 minutos; la mediana (el valor central una vez ordenados) no. Por eso en datos con valores extremos, como sueldos o precios de pisos, se suele dar la mediana. La desviación típica mide cuánto se alejan los datos de la media: cuanto más pequeña, más regular eres. quantiles(n=4) devuelve los tres cuartiles, los puntos que dividen los datos en cuatro partes.

pathlib y os: archivos y carpetas

pathlib representa rutas como objetos y es la opción moderna; ya la viste en ficheros. Vamos a usarla para algo práctico: preparar una carpeta con archivos de prueba y listarlos.

from pathlib import Path

carpeta = Path("descargas")
carpeta.mkdir(exist_ok=True)
for nombre in ["foto1.jpg", "apuntes.pdf", "foto2.jpg", "notas.txt"]:
    (carpeta / nombre).write_text("contenido de prueba", encoding="utf-8")

for archivo in sorted(carpeta.iterdir()):
    if archivo.is_file():
        print(archivo.name, archivo.suffix, archivo.stat().st_size, "bytes")
# apuntes.pdf .pdf 19 bytes
# foto1.jpg .jpg 19 bytes
# foto2.jpg .jpg 19 bytes
# notas.txt .txt 19 bytes

print([p.name for p in sorted(carpeta.glob("*.jpg"))])   # ['foto1.jpg', 'foto2.jpg']
  • iterdir() recorre lo que hay en la carpeta (archivos y subcarpetas); is_file() se queda solo con los archivos.
  • glob("*.jpg") filtra por patrón; rglob busca también en subcarpetas.
  • stat().st_size da el tamaño en bytes.
  • Path.cwd() devuelve la carpeta actual y Path.home() la carpeta personal del usuario.

El módulo os sigue siendo útil para hablar con el sistema operativo. Lo que más usarás son las variables de entorno, valores de configuración que se definen fuera del programa (claves de API, modo de depuración):

import os

modo = os.environ.get("MODO", "desarrollo")   # valor por defecto si no existe
print(modo)                                   # desarrollo

Consejo: para rutas, prefiere pathlib a os.path. Verás os.path.join, os.listdir y os.path.exists en código antiguo; hacen lo mismo que /, iterdir() y exists().

sys: el intérprete y la línea de comandos

sys.argv es la lista de argumentos con que se lanzó el programa desde la terminal. El primero es siempre el nombre del script. sys.exit termina el programa; por convención, el código 0 significa “todo bien” y cualquier otro número, error.

# saludo.py
import sys

if len(sys.argv) < 2:
    print("Uso: python saludo.py NOMBRE")
    sys.exit(1)

nombre = sys.argv[1]
print(f"Hola, {nombre}")
python saludo.py Lucía     # Hola, Lucía
python saludo.py           # Uso: python saludo.py NOMBRE

Este bloque no tiene sentido ejecutarlo en la web, porque aquí no hay terminal desde la que pasar argumentos. Otros datos útiles de sys:

import sys

print(sys.version_info >= (3, 10))   # True (si tu Python es 3.10 o superior)

sys.version_info sirve para comprobar la versión, y sys.platform dice el sistema ("win32", "linux", "darwin" en macOS). Para programas con muchas opciones (--ayuda, -v), el módulo argparse genera la ayuda y valida los argumentos por ti.

collections: estructuras de datos extra

Counter: contar cosas

from collections import Counter

resena = "muy buena comida y muy buen servicio la comida llegó caliente"
palabras = Counter(resena.split())
print(palabras.most_common(2))   # [('muy', 2), ('comida', 2)]

palabras.update(["comida", "postre"])   # suma más apariciones
print(palabras["comida"], palabras["pizza"])   # 3 0

Un Counter es un diccionario especial: si preguntas por algo que no está, devuelve 0 en lugar de lanzar KeyError.

defaultdict: diccionarios con valor inicial

Con defaultdict(int), cada clave nueva empieza valiendo 0; con defaultdict(list), una lista vacía. Así acumulas sin comprobar si la clave existe:

from collections import defaultdict

gastos = [("comida", 12.5), ("ocio", 20), ("comida", 8), ("transporte", 3.4)]
por_categoria = defaultdict(float)
for categoria, importe in gastos:
    por_categoria[categoria] += importe
print(dict(por_categoria))   # {'comida': 20.5, 'ocio': 20.0, 'transporte': 3.4}

namedtuple: tuplas con nombre

Una tupla con nombre se comporta como una tupla normal, pero sus elementos también se leen por nombre. Es ideal para datos pequeños que no cambian:

from collections import namedtuple

Punto = namedtuple("Punto", ["lat", "lon"])
cima = Punto(43.17, -4.85)
print(cima.lat, cima[1])   # 43.17 -4.85
print(cima._asdict())      # {'lat': 43.17, 'lon': -4.85}

Si además necesitas métodos o valores por defecto, una dataclass suele ser mejor opción.

deque: cola por los dos extremos

Una deque (double-ended queue, se pronuncia “dek”) permite añadir y quitar por ambos lados de forma rápida. Con maxlen se queda solo con los últimos elementos, perfecto para un historial:

from collections import deque

historial = deque(maxlen=3)
for pagina in ["inicio", "cursos", "python", "retos"]:
    historial.append(pagina)
print(historial)   # deque(['cursos', 'python', 'retos'], maxlen=3)

cola = deque(["Ana", "Luis"])
cola.append("Eva")          # llega alguien al final
print(cola.popleft())       # Ana (atiende al primero)

En una lista, pop(0) tiene que mover todos los elementos; en una deque, popleft() es instantáneo aunque haya millones.

itertools: combinar y agrupar

product: todas las combinaciones de varias listas

import itertools

tallas = ["S", "M"]
colores = ["negro", "blanco"]
for talla, color in itertools.product(tallas, colores):
    print(f"Camiseta {talla} {color}")
# Camiseta S negro
# Camiseta S blanco
# Camiseta M negro
# Camiseta M blanco

Equivale a dos for anidados, pero se lee mejor y funciona con cualquier número de listas.

combinations: grupos sin orden

¿Qué partidos hay en una liguilla de cuatro equipos si todos juegan contra todos una vez?

equipos = ["Racing", "Gimnástica", "Laredo", "Escobedo"]
partidos = list(itertools.combinations(equipos, 2))
print(len(partidos))   # 6
print(partidos[0])     # ('Racing', 'Gimnástica')

Si el orden importa (ida y vuelta), usa permutations, que da 12.

groupby: agrupar elementos consecutivos

groupby agrupa elementos seguidos que comparten una clave. Por eso casi siempre hay que ordenar antes por esa misma clave:

ventas = [("enero", 120), ("febrero", 80), ("enero", 45), ("febrero", 30), ("marzo", 60)]
ventas.sort(key=lambda v: v[0])

for mes, grupo in itertools.groupby(ventas, key=lambda v: v[0]):
    print(mes, sum(importe for _, importe in grupo))
# enero 165
# febrero 110
# marzo 60

Sin el sort, “enero” aparecería dos veces. Para agrupar sin ordenar, el defaultdict de antes es más sencillo.

time: medir y esperar

time.perf_counter() devuelve un contador de alta precisión. Restando dos lecturas sabes cuánto ha tardado un trozo de código:

import time

inicio = time.perf_counter()
total = sum(range(1_000_000))
duracion = time.perf_counter() - inicio
print(f"Suma: {total}, tiempo: {duracion:.4f} s")
# Suma: 499999500000, tiempo: 0.0123 s  (el tiempo varía en cada equipo)

time.sleep(segundos) pausa el programa, útil para no saturar un servidor con peticiones seguidas. time.time() da los segundos desde 1970 (la llamada marca de tiempo Unix). Para trabajar con fechas legibles usa datetime.

shutil: copiar, mover y borrar en bloque

pathlib crea, lee y borra archivos sueltos; shutil hace operaciones de más alto nivel. Con la carpeta descargas de antes, vamos a ordenar los archivos en subcarpetas según su extensión:

import shutil

for archivo in list(carpeta.iterdir()):
    if archivo.is_file():
        destino = carpeta / archivo.suffix[1:]     # "jpg", "pdf", "txt"
        destino.mkdir(exist_ok=True)
        shutil.move(archivo, destino / archivo.name)

print(sorted(str(p.relative_to(carpeta)) for p in carpeta.rglob("*.*")))
# ['jpg/foto1.jpg', 'jpg/foto2.jpg', 'pdf/apuntes.pdf', 'txt/notas.txt']

shutil.copytree(carpeta, "copia_descargas", dirs_exist_ok=True)   # copia la carpeta entera
shutil.rmtree("copia_descargas")                                  # y la borra con todo su contenido
FunciónQué hace
shutil.copy(origen, destino)Copia un archivo
shutil.copytree(origen, destino)Copia una carpeta con todo su contenido
shutil.move(origen, destino)Mueve o renombra
shutil.rmtree(carpeta)Borra una carpeta y todo lo que tiene
shutil.make_archive("copia", "zip", carpeta)Crea un ZIP de una carpeta

Cuidado: shutil.rmtree borra sin pasar por la papelera y sin preguntar. Comprueba bien la ruta antes de ejecutarlo, sobre todo si la construyes con variables.

Errores frecuentes

  • Llamar a tu archivo como un módulo (random.py, statistics.py): tu archivo tapa al original y aparecen errores como module 'random' has no attribute 'seed'.
  • Comparar decimales con ==: usa math.isclose.
  • Usar random para contraseñas: usa secrets.
  • Olvidar ordenar antes de groupby: el mismo grupo sale varias veces.
  • Confundir choices y sample: choices puede repetir elementos; sample, no.
  • Leer sys.argv[1] sin comprobar la longitud: si no pasan argumentos, IndexError.
  • Escribir rutas con "\\" o "/" a mano: usa Path y el operador /.

Resumen

MóduloPara qué lo usarás
mathceil, isclose, dist, prod, gcd, inf
randomseed, randint, choice, choices con pesos, shuffle, sample
secretsValores aleatorios seguros (tokens, contraseñas)
statisticsmean, median, mode, stdev, quantiles
pathlibRutas, iterdir, glob, mkdir, tamaño de archivos
osVariables de entorno con os.environ.get
sysargv, exit, version_info, platform
collectionsCounter, defaultdict, namedtuple, deque
itertoolsproduct, combinations, permutations, groupby
timeperf_counter para medir, sleep para esperar
shutilCopiar, mover y borrar carpetas enteras

La biblioteca estándar es enorme y no hace falta memorizarla: basta con saber que existe y buscar cuando lo necesites. En la siguiente lección aprenderás a buscar y validar patrones de texto con expresiones regulares.

Pon a prueba lo que has aprendido

[Python] ¿Qué imprime este código?
from collections import Counter

c = Counter("la casa de la luna".split())
print(c["la"], c["sol"])

[Python] Se ejecuta itertools.groupby sobre los datos [("a", 1), ("b", 2), ("a", 3)] con la clave la primera letra, sin ordenar antes. ¿Qué claves de grupo salen?

[Python] ¿Qué contiene la deque al final?
from collections import deque

d = deque(maxlen=3)
for x in [1, 2, 3, 4, 5]:
    d.append(x)
print(d)

[Python] ¿Qué imprime este código y por qué?
import math

print(0.1 + 0.2 == 0.3, math.isclose(0.1 + 0.2, 0.3))

¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.