Aprende leyendo en orden

collections — Counter / defaultdict / deque / namedtuple

Aprende a contar ocurrencias con Counter, manejar claves faltantes con defaultdict, usar deque para operaciones O(1) en ambos extremos, y registros con nombre vía namedtuple.

El módulo collections reúne estructuras de datos especializadas que cubren los huecos que dejan list / dict / tuple. Este artículo recorre, en orden, las cuatro a las que más recurrirás en proyectos reales: Counter / defaultdict / deque / namedtuple.

Para qué sirve cada una de las cuatro

Cada una existe para simplificar un caso que resulta incómodo de escribir con los tipos integrados. La tabla siguiente te da la vista panorámica, y luego cada sección entra al detalle de cómo usarlas.

Los cuatro tipos de collections
CounterCuenta ocurrenciasdefaultdictAuto-init claves faltantesdequeO(1) en ambos extremosnamedtupleNombra los campos de tupla
Counter es para contar, defaultdict gestiona claves faltantes, deque ofrece operaciones rápidas en ambos extremos, y namedtuple añade nombres a las tuplas. Las cuatro son tipos de extensión que añaden comodidad sobre los list / dict / tuple normales.
TipoProblema que resuelveAlternativa simple
CounterContar elementos uno a uno con un bucle es verbosofor + tally con dict
defaultdictNecesitas if x not in d: d[x] = ... antes de usar una clave nuevadict.setdefault(...)
dequelist.insert(0, ...) y list.pop(0) son lentos (O(n))list (vale para datos pequeños)
namedtupleLos elementos de una tupla solo son accesibles por posicióndataclass o dict (más pesado)

Counter — cuenta ocurrencias en una línea

Counter es una clase que toma un iterable (una lista, una cadena, etc.) y devuelve un dict con cuántas veces aparece cada elemento. Con un dict normal tendrías que escribir un bucle que comprueba si la clave ya existe, la inicializa a 1 si no, o la incrementa en caso contrario — pero Counter hace lo mismo en una línea: Counter(list).

El valor de retorno es una subclase de dict, así que puedes obtener valores con counter["apple"] igual que en un dict normal. También está .most_common(N), que devuelve una lista de tuplas (elemento, cuenta) en orden descendente — perfecto para mostrar rankings.

Cómo funciona Counter
List[apple, banana, apple, ...]Counter(list)Counter{apple: 3, banana: 2, ...}cuenta
Solo pasa un iterable y obtienes una subclase de dict con la cuenta de ocurrencias por elemento. most_common(N) devuelve los N principales por frecuencia, así que puedes escribir un ranking en una sola línea.

Cuenta seis registros de compra por producto usando Counter. Siente cómo una sola línea reemplaza un bucle for normal.

① Importa Counter desde collections

② Define la lista de compras ["apple", "banana", "apple", "cherry", "banana", "apple"]

③ Pasa la lista a Counter para construir el conteo e imprímelo como Cuenta: ◯

④ Imprime la cuenta de apple como cuenta de apple: ◯ (puedes usar el mismo acceso con corchetes que en dict)

⑤ Usa most_common para obtener los 2 principales e imprímelos como Top 2: ◯

(Si tu código se ejecuta correctamente, aparecerá la explicación.)

Editor Python

Ejecutar el código para ver el resultado

defaultdict — auto-inicializa claves faltantes

defaultdict es un dict que inserta automáticamente un valor por defecto cuando accedes a una clave que aún no existe. Con un dict normal tienes que escribir el ritual de comprobación de existencia + inicialización cada vez, como if key not in d: d[key] = []. Con defaultdict, pasas una función que produce el valor inicial al crearlo, y la llama automáticamente la primera vez que se toca una clave faltante.

Los dos patrones que verás más a menudo son defaultdict(list) (lista vacía para claves faltantes) y defaultdict(int) (0 para claves faltantes). El argumento es una función que devuelve el valor inicial al ser llamada sin argumentoslist y int cumplen (devuelven [] y 0 respectivamente), así que los pasas directamente.

dict vs. defaultdict
dict normald["x"] (sin clave)→ KeyErrorNecesitas:if x not in d: d[x] = []defaultdict(list)d["x"] (sin clave)→ Crea [] autoSolo llama a .append(...)directamente
El dict normal lanza KeyError ante claves faltantes. defaultdict llama a la función que pasaste al construirlo y usa el resultado. Pasa list para una lista vacía, int para 0, set para un conjunto vacío.

Cuándo elegir Counter vs. defaultdict

Si solo quieres contar cosas, Counter es más conciso (y obtienes most_common gratis). Si estás agrupando en listas o conjuntos — cualquier caso donde el valor inicial sea un contenedor vacíodefaultdict(list) / defaultdict(set) encaja mejor.

Agrupa frutas por color. Con defaultdict(list), la primera vez que toques una clave de color, se crea automáticamente una lista vacía, así que puedes llamar a .append sin comprobación de existencia.

① Importa defaultdict desde collections

② Crea un defaultdict cuyo valor por defecto sea una lista vacía

③ Registra estas tres entradas en orden:

- "red""apple"

- "yellow""banana"

- "red""cherry"

Convierte a un dict normal e imprime como Por color: ◯ (imprimir el defaultdict directamente da un repr menos legible)

Editor Python

Ejecutar el código para ver el resultado

deque — operaciones O(1) en ambos extremos

deque (double-ended queue, cola de doble extremo) es una estructura de datos parecida a list que admite añadir y eliminar por ambos extremos. La list normal ya gestiona operaciones por la derecha vía append y pop, pero list.insert(0, x) y list.pop(0) desplazan internamente cada elemento una posición, así que terminan siendo O(n) (más lentas a medida que crece la lista).

deque está diseñada para que ambos extremos sean O(1) (tiempo constante independientemente de la longitud), lo que la convierte en la elección correcta para colas, buffers de historial y mantener las N entradas más recientes — cualquier sitio donde necesites añadir y quitar por ambos lados. El argumento maxlen es especialmente útil: fijas una longitud máxima, y los añadidos por encima de ese límite descartan silenciosamente el elemento del extremo opuesto, dándote un buffer circular gratis.

Coste de operación: list vs. deque
listAdd / remove por la izquierdaDesplaza cada elementouna posición→ O(n) lentodequeappend / pop por ambos ladosSolo manipulaciónde punteros→ O(1) rápido
Las operaciones por la izquierda en list son O(n) (cada elemento se desplaza), mientras que deque es O(1) en ambos extremos. Con deque(maxlen=N), obtienes un buffer circular que descarta automáticamente el más antiguo cuando está lleno.
MétodoEfectoCoste
append(x)Añade por la derechaO(1)
appendleft(x)Añade por la izquierdaO(1)
pop()Elimina por la derechaO(1)
popleft()Elimina por la izquierdaO(1)
maxlen=NDefine longitud máxima (buffer circular)El extremo opuesto se descarta automáticamente
Métodos principales de deque
appendleft(x)deque[a, b, c]append(x)popleft()pop()
Contra el deque central, el extremo izquierdo usa appendleft / popleft y el extremo derecho usa append / pop. Las direcciones de las flechas muestran añadir (hacia el deque) vs. eliminar (desde el deque).

Mantén los 3 últimos logs de acceso en un deque(maxlen=3). Tras 5 appends, solo sobreviven los 3 últimos.

① Importa deque desde collections

② Crea un deque vacío con longitud máxima 3

Haz append de los enteros del 0 al 4 en orden (5 en total)

④ Imprime el resultado como Últimos 3: ◯ (los 0 y 1 antiguos deberían haber sido expulsados)

Editor Python

Ejecutar el código para ver el resultado

Extrae la cabeza y la cola de una cola de tareas. Usa popleft y pop para alternar el extremo del que sacas.

① Importa deque desde collections

② Construye un deque con las cuatro tareas ["Tarea A", "Tarea B", "Tarea C", "Tarea D"]

③ Usa popleft() para obtener la primera tarea e imprímela como Primera tarea: ◯

④ Usa pop() para obtener la última tarea e imprímela como Última tarea: ◯

⑤ Imprime el deque restante como Restantes: ◯

Editor Python

Ejecutar el código para ver el resultado

namedtuple — registros ligeros que nombran los campos de tupla

namedtuple es una función que define una tupla con campos nombrados en una línea. Una tupla normal como (3, 4) es solo accesible por posición (p[0] / p[1]), así que el lector tiene que recordar qué posición es cuál. namedtuple te permite escribir p.x / p.y con nombres significativos, lo que es una gran mejora de legibilidad.

Además se mantiene compatible con las tuplas normales — el indexado p[0] y el desempaquetado *p siguen funcionando — así que puedes añadir nombres a código existente basado en tuplas sin romper nada. Es una extensión ligera.

namedtuple de un vistazo
Point = namedtuple('Point', ['x', 'y'])p = Point(3, 4)Por nombrep.x / p.yPor índicep[0] / p[1]Como dictp._asdict()
Define el equivalente a una clase en una línea, luego usa acceso posicional (p[0]) y por nombre (p.x). _asdict() convierte a un dict, lo que se lleva bien con JSON y pprint.

namedtuple vs. dataclass

Si solo necesitas un registro ligero inmutable, recurre a namedtuple. Si quieres métodos, valores por defecto y type hints detalladas, dataclass (cubierta dos artículos más adelante) es la herramienta correcta. La fortaleza de namedtuple es la compatibilidad con tuplas — perfecta para reemplazar tipos de retorno existentes como def f() -> tuple[int, int]: sin romper a quien la llama.

Define una namedtuple Point y lee las coordenadas por nombre.

① Importa namedtuple desde collections y crea una con nombre de clase "Point" y campos ["x", "y"]

② Construye una instancia p con x=3, y=4

③ Imprime como x: 3 y: 4 usando acceso por nombre p.x y p.y

Editor Python

Ejecutar el código para ver el resultado

Extras de namedtuple — repr / index / _asdict

Además del acceso por nombre estilo clase, namedtuple te da acceso por índice compatible con tuplas, un repr legible y un método _asdict para conversión a dict. Los usarás para depuración, compatibilidad con APIs existentes y serialización JSON, entre otras cosas.

Toma el p de la Práctica 1 y prueba los tres patrones de acceso adicionales.

print("repr:", p) para imprimir como repr: Point(x=3, y=4)

② Usa p[0] y p[1] para acceso por índice e imprime como Por índice: 3 4

③ Usa p._asdict() para imprimir como Como dict: {'x': 3, 'y': 4}

Editor Python

Ejecutar el código para ver el resultado
QUIZ

Verificación de conocimientos

Responde cada pregunta una a una.

Pregunta 1¿Cuál es la forma más concisa de obtener la cuenta de ocurrencias por elemento de una lista nums en una sola línea?

Pregunta 2Tras crear defaultdict(list), ¿qué te da el primer acceso a una clave nueva d["x"]?

Pregunta 3¿Qué encaja mejor cuando quieres mantener solo los 5 elementos más recientes?