Elegir la Estructura de Datos Correcta
Elige contenedores según cómo lees, actualizas y recorres los datos, no por costumbre. La estructura incorrecta cuesta claridad y rendimiento asintótico; la correcta hace que el código sea más corto y rápido sin micro-optimización.
Receta
Haz cuatro preguntas antes de codificar:
- ¿Necesito pertenencia rápida por valor o clave?
- ¿Se debe preservar el orden o poder ordenar?
- ¿Se permiten duplicados?
- ¿Son los datos tabulares (millones de filas, operaciones de columna)?
Mapea las respuestas a dict, set, list, deque, heapq o bibliotecas de dataframes.
Cuándo recurrir a esto:
- Diseñar el modelo en memoria de un nuevo módulo
- Refactorizar listas anidadas en estructuras más claras
- Revisiones de rendimiento que muestran escaneos O(n) en rutas críticas
- Discusiones de arquitectura antes de la elección de persistencia
Ejemplo de Trabajo
from collections import defaultdict, deque
import heapq
# Patrón: indexar usuarios por id -> dict
users_by_id: dict[int, dict] = {1: {"name": "Ada"}, 2: {"name": "Linus"}}
# Patrón: eliminar duplicados de etiquetas -> set
tags = set(["py", "ml", "py"])
# Patrón: cola de trabajos FIFO -> deque
jobs: deque[str] = deque(["a", "b"])
# Patrón: planificación por prioridad -> heapq
heap: list[tuple[int, str]] = []
heapq.heappush(heap, (2, "low"))
heapq.heappush(heap, (1, "high"))
# Patrón: agrupar filas -> defaultdict
by_role: defaultdict[str, list[str]] = defaultdict(list)
for name, role in [("Ada", "admin"), ("Linus", "dev")]:
by_role[role].append(name)Lo que esto demuestra:
- Dict para búsqueda por clave
- Set para unicidad y pertenencia
- Deque para FIFO sin penalización de
pop(0) - Heap para ordenación por prioridad
- defaultdict para agrupar sin código repetitivo
Análisis Profundo
Tabla de Decisiones
| Patrón de acceso | Preferir | Por qué |
|---|---|---|
| Búsqueda por clave única | dict | O(1) en promedio |
| Prueba de pertenencia | set | O(1) en promedio |
| Añadir ordenado + escanear | list | Simple, amigable con la caché |
| Cola/pila en ambos extremos | deque | Pops O(1) |
| Top-K / programar | heapq | Actualizaciones O(log n) |
| Analítica columnar | pandas/polars | Operaciones vectorizadas |
Cómo Funciona
- La asintótica importa a escala - Los escaneos O(n) perjudican a 10^6 elementos, están bien a 50.
- Las constantes importan a n pequeño - La sobrecarga de Dict puede perder frente al escaneo de lista para colecciones muy pequeñas.
- La inmutabilidad señala la intención - Tuple/frozenset documentan la estabilidad y permiten el hash.
- La composición supera al "talla única" - Grafo como
dict[nodo, set[vecino]]combina dict + set. - Límite de persistencia - La elección en memoria difiere de la decisión SQL/Redis.
Notas de Python
# olor: búsqueda lineal repetida
if item in big_list: # O(n) cada vez
# arreglo: construir set una vez
seen = set(big_list)
if item in seen: # O(1) en promedioTrampas
- list como set -
if x in itemsen una lista grande en un bucle → O(n²). Arreglo: Preconstruirset(items). - dict cuando una lista de pares es suficiente - Los registros de dos elementos a veces son mejores como tuplas en una lista. Arreglo: Simplificar hasta que se necesite búsqueda por clave.
- pandas para 20 filas - Pesada importación y sobrecarga de API. Arreglo: Diccionario/lista simple hasta cientos de filas o operaciones de columna.
- OrderedDict por defecto - Innecesario ya que dict está ordenado. Arreglo: Diccionario simple a menos que
move_to_end. - Heap prematuro - Cinco elementos ordenados una vez →
sorted. Arreglo: heap cuando k << n o en streaming.
Alternativas
| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
| SQLite en memoria | Consultas relacionales sobre datos moderados | Contador simple |
| Redis | Caché compartida entre procesos | Script de un solo proceso |
| Modelos ORM | Entidades de dominio persistentes | Búfer algorítmico efímero |
Clases slots | Muchos objetos pequeños de campos fijos | Bloques JSON dinámicos |
Preguntas Frecuentes
¿list o deque para pila?
list.append/pop está bien solo para pila. Usa deque al hacer pop desde la izquierda o ambos extremos.
¿Cuándo pandas sobre dict de listas?
Estadísticas por columna, uniones, filtrado en millones de filas - no para un diccionario de configuración de 30 claves.
¿tuple o dataclass?
Tuple/NamedTuple para pequeños paquetes inmutables. Dataclass cuando se necesiten valores predeterminados, métodos o mutabilidad.
¿clave de dict: str o int?
Elige la clave natural del dominio. Los IDs enteros son más rápidos y pequeños que los IDs convertidos a cadena cuando son numéricos.
¿Cómo modelar un grafo?
dict[nodo, list[vecino]] o defaultdict(set) para grafos dispersos. NetworkX para la biblioteca de algoritmos.
¿Counter vs dict para recuentos?
Counter al hacer matemáticas de multiconjuntos o most_common. Diccionario simple está bien para un recuento de una sola pasada.
¿bisect o heap?
bisect para inserciones mayormente ordenadas; heap para extracción dinámica de mínimo/máximo.
¿Cuándo inmutabilidad?
Claves de diccionario, elementos de conjunto, cachés hash, configuración compartida de solo lectura - tuple/frozenset/bytes.
¿Importa el orden para set?
No hay orden semántico - si el orden de visualización importa, usa lista u el patrón OrderedDict con dict.fromkeys.
¿Cómo documentar la elección?
Comentario de una línea o ADR cuando no sea obvio - los lectores futuros heredan el contexto.
Relacionados
- Conceptos Básicos de Estructuras de Datos - Resumen de Big-O
- Módulo collections - contenedores especializados
- Inmutabilidad y Hashabilidad - restricciones clave
- heapq y Colas de Prioridad - planificación
Versiones de Stack: Esta página fue escrita para Python 3.14.0 (estable 3.14, mantenimiento 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, y uv 0.6+.