1.14 · Biblioteca estándar¶
Objetivos. Al terminar este capítulo podrás: localizar una solución en la
biblioteca estándar antes de programarla desde cero; trabajar con fechas,
CSV y contadores; y seleccionar módulos adecuados para una tarea sin añadir
dependencias innecesarias. Como ampliación: componer iteradores con itertools
y reutilizar funciones con functools.
Evidencia de logro. Construirás un informe reproducible a partir de datos de ventas que use módulos estándar para fechas, CSV y conteos, explicando qué parte del problema resuelve cada módulo.
Contexto y motivación¶
Python incluye una colección amplia de módulos que se instala junto con el intérprete. Es la biblioteca estándar. Antes de buscar un paquete externo conviene comprobar si la solución que necesitas ya existe: reduces dependencias, instalación y superficie de mantenimiento.
Este capítulo reúne herramientas que aparecen con frecuencia al construir
scripts y aplicaciones. pathlib y re ya se introdujeron en 1.9 y 1.10; aquí
se combinan con otros módulos en un flujo completo.
Vocabulario¶
| Término | Significado |
|---|---|
| Biblioteca estándar | Módulos incluidos con Python |
| CSV | Formato tabular separado por delimitadores |
Counter |
Contador especializado de elementos |
| Iterador | Objeto que entrega valores uno a uno |
itertools |
Herramientas para componer iteradores |
| Fecha ingenua | Fecha/hora sin zona horaria asociada |
| Variable de entorno | Configuración proporcionada por el sistema |
Prerrequisitos¶
1.6 · Listas y tuplas, 1.9 · Cadenas, 1.10 · Archivos y 1.13 · POO.
1. Elegir un módulo antes de escribir código¶
Relaciona primero la tarea con una herramienta:
| Necesidad | Módulo o tipo | Uso habitual |
|---|---|---|
| Cálculos matemáticos | math |
sqrt, ceil, isclose |
| Elección reproducible | random.Random |
simulaciones y pruebas |
| Fechas | datetime |
date, timedelta, datetime |
| Rutas | pathlib.Path |
ya tratado en 1.10 |
| CSV | csv |
DictReader, DictWriter |
| Frecuencias | collections.Counter |
contar y obtener los más frecuentes |
| Agrupar o encadenar | itertools |
procesar sin materializar todo |
| CLI | argparse |
argumentos de terminal |
| Sistema operativo | os |
entorno y operaciones heredadas |
La tabla sirve como guía para buscar una API concreta y leer su documentación antes de usarla; no es necesario importar todos los módulos.
2. Matemáticas y aleatoriedad controlada¶
math aporta funciones que no forman parte de los operadores básicos:
import math
print(math.sqrt(81))
print(math.ceil(2.1), math.floor(2.9))
print(math.isclose(0.1 + 0.2, 0.3, rel_tol=1e-9))
Salida esperada:
La comparación 0.1 + 0.2 == 0.3 puede fallar por la representación binaria de
los decimales. math.isclose expresa mejor la intención cuando comparas
resultados numéricos aproximados.
Para pruebas o simulaciones crea un generador local con una semilla:
import random
azar = random.Random(42)
print(azar.randint(1, 6))
print(azar.sample(["A", "B", "C", "D"], k=2))
# La misma semilla, otro generador: mismos numeros.
otro_azar = random.Random(42)
print(otro_azar.randint(1, 6))
print(otro_azar.sample(["A", "B", "C", "D"], k=2))
Salida esperada:
Las dos últimas líneas repiten exactamente las dos primeras. Eso es lo que
compra la semilla: dos ejecuciones del mismo programa dan el mismo resultado, y
por eso un test que dependa de números aleatorios puede comprobarse. Fíjate
también en que azar y otro_azar son objetos distintos: cada
random.Random(semilla) tiene su propio estado, así que crear uno no interrumpe
al otro. Usar el generador global (random.randint(...) sin más) sí dependería
del orden en que se ejecutara el resto del programa.
Lo que no te da la semilla es compatibilidad entre versiones de Python: el
algoritmo de random.sample puede cambiar, así que la lista concreta
['A', 'D'] es la salida de esta versión. Si tu programa guarda ese resultado
en un fichero o lo compara en un test, no dependas del orden; extrae, ordena o
comprueba la longitud.
Una semilla no hace seguro un sistema aleatorio
random sirve para simulaciones y pruebas, nunca para contraseñas, tokens ni
identificadores de sesión: su algoritmo es público y se puede predecir. Para
eso está el módulo secrets, que usa el generador del sistema operativo.
3. Fechas y duraciones¶
datetime representa fechas y permite operar con duraciones:
from datetime import date, timedelta
inicio = date(2026, 8, 21)
revision = inicio + timedelta(days=10)
print(inicio.isoformat())
print(revision.isoformat())
print((revision - inicio).days)
Salida esperada:
Para una fecha sin hora, date es suficiente. Cuando trabajes con horas reales,
zonas horarias o APIs, usa datetime consciente de zona y documenta qué zona
representa. No mezcles fechas como cadenas si necesitas ordenar o calcular.
Fechas y zonas horarias
datetime.now() sin zona describe la hora local del proceso, no una hora
universal. Para sistemas distribuidos conviene acordar UTC o una zona
explícita y convertir solo al mostrarla.
4. Leer CSV con estructura¶
Si un fichero tabular es sencillo, csv.DictReader evita separar cadenas a mano:
import csv
from io import StringIO
contenido = "producto,importe\nlibro,12.5\nteclado,45.0\nlibro,7.5\n"
filas = csv.DictReader(StringIO(contenido))
ventas = [{**fila, "importe": float(fila["importe"])} for fila in filas]
for venta in ventas:
print(venta["producto"], venta["importe"])
print("Total:", sum(venta["importe"] for venta in ventas))
Salida esperada:
Dos construcciones de este bloque merecen una pausa, porque son distintas de lo que ya conoces:
StringIOes un fichero que vive en memoria.contenidoes una cadena, yStringIO(contenido)la envuelve en algo que se comporta como un fichero abierto. Eso permite probar un lector de CSV sin escribir nada en disco, y por eso aparece en los tests. Para trabajar con un fichero de verdad, sustitúyelo por la ruta real.{**fila, "importe": ...}fusiona dos diccionarios. El**delante del nombre no es aquí «pasar argumentos» (como en**kwargs), sino «desempaqueta este diccionario dentro del nuevo». El resultado copia las claves defilay añadeimporteya convertido a número, sustituyendo el texto que traía. El orden importa: la clave"importe"va después de**fila, así que gana la nueva. Si la pones antes, se perdería la conversión.
DictReader usa la primera fila como nombres de campo y entrega cada fila como
un diccionario. Los valores llegan como texto, por lo que la conversión a
float es una decisión explícita. Para un fichero real sustituirías StringIO
por Path(...).open(newline="", encoding="utf-8"); newline="" deja que el
módulo csv gestione los saltos de línea, como recomienda su documentación.
5. collections: contar y agrupar¶
Counter expresa directamente un problema de frecuencias:
from collections import Counter, defaultdict
productos = ["libro", "teclado", "libro", "ratón", "libro"]
frecuencias = Counter(productos)
print(frecuencias.most_common(2))
por_categoria = defaultdict(list)
por_categoria["entrada"].append("teclado")
por_categoria["entrada"].append("ratón")
print(dict(por_categoria))
Salida esperada:
defaultdict(list) crea la lista al acceder a una clave nueva. Úsalo cuando
esa creación automática sea parte de la intención; si una clave ausente es un
error, dict normal y get comunican mejor el contrato.
6. Ampliación · itertools: procesar sin crear colecciones innecesarias¶
Este apartado y el siguiente son ampliación: estúdialos después de terminar el núcleo del capítulo (apartados 1–5 y 8) y el producto evaluable.
Los iteradores permiten encadenar datos y consumir solo la parte necesaria:
from itertools import chain, islice
fuentes = [["a", "b"], ["c"], ["d", "e"]]
primera_parte = list(islice(chain.from_iterable(fuentes), 4))
print(primera_parte)
Salida esperada:
chain.from_iterable recorre varias colecciones como una sola y islice toma
los primeros elementos sin construir antes la secuencia completa. Es útil en
pipelines de datos grandes, aunque una lista sencilla puede ser más legible en
un caso pequeño.
Ampliación: deque y pairwise
collections.deque es adecuado para una cola con entradas y salidas por
ambos extremos. itertools.pairwise recorre parejas consecutivas. No los
introduzcas solo para sustituir una lista: elige la estructura cuando el
patrón de acceso lo justifique.
7. Ampliación · functools y funciones reutilizables¶
En 1.8 viste decoradores y lru_cache. Otra utilidad breve es partial, que
fija parte de los argumentos de una función:
from functools import partial
redondear_dos = partial(round, ndigits=2)
print(redondear_dos(3.14159))
Salida esperada:
Si el nombre redondear_dos es más claro que la llamada parametrizada, partial
puede mejorar la composición. Si la función resultante necesita una explicación
larga, define una función con def y un docstring.
8. Variables de entorno y sistema¶
os.environ permite leer configuración proporcionada por el entorno:
Salida esperada:
La salida mostrada corresponde a un equipo donde ASISTENTE_MODO no está
definida; si estuviera definida, os.environ.get leería ese valor.
No guardes claves secretas en el código ni en el repositorio. En 1.15 se usará
python-dotenv como comodidad local; el principio es el mismo: la configuración
entra desde fuera del código.
8.1 Ampliación: argparse y subprocess¶
Para una CLI, argparse valida y documenta argumentos sin analizar sys.argv a
mano:
Este bloque va en un fichero cli.py, no en una celda del cuaderno. La
razón es concreta: parse_args() lee los argumentos con los que se arrancó el
programa desde la terminal, y si no entiende alguno termina el programa
entero con un código de error. En un cuaderno o en el REPL no hay argumentos
de terminal que leer, y ese final brusco interrumpiría la sesión.
# cli.py
import argparse
parser = argparse.ArgumentParser(description="Resume notas")
parser.add_argument("--limite", type=int, default=5)
args = parser.parse_args()
print(f"Límite: {args.limite}")
Comprobación desde la terminal:
Salida esperada:
Si lo llamas sin --limite, se usa el valor por defecto que declaraste:
Salida esperada:
Y si le das algo que no es un entero, argparse avisa y termina en lugar
de continuar con un valor raro:
Salida esperada:
Ahí está el valor de type=int: el error sale con un mensaje que señala la
opción culpable, no un ValueError de Python en una línea sin contexto. Esa es la
diferencia entre validar los argumentos al arrancar o descubrirlos a mitad del
programa.
subprocess puede ejecutar programas externos, pero una entrada de usuario no
debe convertirse sin control en un comando. Evita shell=True salvo que
comprendas y controles todas las implicaciones; esta ampliación no es necesaria
para el proyecto de la unidad.
Aplicación práctica: informe reproducible de ventas¶
Integraremos varias herramientas sin instalar nada:
import csv
from collections import Counter
from datetime import date
from io import StringIO
contenido = (
"producto,categoria,importe\n"
"libro,lectura,12.5\n"
"teclado,entrada,45.0\n"
"libro,lectura,7.5\n"
"monitor,oficina,180.0\n"
)
filas = csv.DictReader(StringIO(contenido))
ventas = [{**fila, "importe": float(fila["importe"])} for fila in filas]
productos = Counter(venta["producto"] for venta in ventas)
total = sum(venta["importe"] for venta in ventas)
print("Fecha:", date(2026, 8, 21).isoformat())
print("Número de ventas:", len(ventas))
print("Total:", total)
print("Más vendido:", productos.most_common(1)[0])
Salida esperada:
La salida es reproducible porque los datos, la fecha y el número de filas están declarados. En una aplicación real documentarías el origen, el formato y la zona horaria de la fecha.
Fíjate en que la fecha se escribe con date(2026, 8, 21) y no con
date.today(): el informe da el mismo resultado dentro de un año. Si quisieras
la fecha real, date.today() la daría, pero entonces la salida cambiaría cada
día y no podrías compararla con la de nadie.
Errores frecuentes¶
- Instalar una dependencia externa antes de buscar una solución estándar.
- Tratar todos los valores leídos de CSV como números sin convertirlos.
- Usar
randompara secretos o tokens. - Comparar fechas como texto cuando el formato no es uniforme.
- Consumir un iterador y después intentar recorrerlo otra vez.
- Usar
defaultdicty crear claves por accidente al consultarlas. - Introducir
subprocessoshell=Truecon texto de usuario sin validación. - Repetir en este capítulo todo el contenido de
pathlibo regex de los anteriores: aquí interesa integrarlos, no memorizarlos de nuevo.
Práctica de transferencia¶
- Lee un CSV con
DictReadery calcula el importe total y el producto más frecuente. - Crea un informe de fechas con una fecha inicial y tres vencimientos usando
timedelta. - Procesa dos listas de eventos con
chainy conserva solo los cinco primeros conislice. - Usa
Counterpara detectar valores repetidos y explica qué información se pierde al convertir directamente aset. - Añade un argumento
--limitea una CLI pequeña y comprueba la validación de un valor no entero.
Qué debes comprobar: en el punto 1, que el total coincide con una suma hecha a
mano; en el 4, qué valores repetidos detecta Counter y que el set solo te
dice cuáles son distintos, no cuántas veces aparece cada uno.
Producto evaluable¶
Crea 10_biblioteca_estandar.py como script o cuaderno marimo. Debe:
- Leer un conjunto de ventas en CSV reproducible.
- Convertir tipos de forma explícita y calcular total, fecha de informe y frecuencias.
- Usar al menos
datetime,csvyCounter(los iteradores deitertoolsquedan para la ampliación). - Mostrar una salida estable y explicar por qué cada módulo es adecuado.
- Separar el núcleo obligatorio de una ampliación opcional (
argparse,dequeo una ruta de fichero real).
Criterio de aceptación: el informe se ejecuta sin paquetes externos, produce la misma salida con los mismos datos y no depende de una ordenación accidental ni de la hora actual del equipo.
Formato de entrega: script o cuaderno marimo ejecutado, con salida esperada, interpretación y conclusión.
Resumen y referencia rápida¶
| Necesitas | API |
|---|---|
| Matemáticas | math.sqrt, math.isclose |
| Aleatoriedad reproducible | random.Random(semilla) |
| Fechas y duración | date, datetime, timedelta |
| CSV tabular | csv.DictReader / DictWriter |
| Frecuencias | collections.Counter |
| Agrupación | collections.defaultdict |
| Iteradores | itertools.chain, islice |
| Funciones parciales | functools.partial |
| Configuración | os.environ.get |
| CLI | argparse.ArgumentParser |
Siguiente: 1.15 · Librerías externas.