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?
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.
list / dict / tuple normales.| Tipo | Problema que resuelve | Alternativa simple |
|---|---|---|
| Counter | Contar elementos uno a uno con un bucle es verboso | for + tally con dict |
| defaultdict | Necesitas if x not in d: d[x] = ... antes de usar una clave nueva | dict.setdefault(...) |
| deque | list.insert(0, ...) y list.pop(0) son lentos (O(n)) | list (vale para datos pequeños) |
| namedtuple | Los elementos de una tupla solo son accesibles por posición | dataclass 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.
most_common(N) devuelve los N principales por frecuencia, así que puedes escribir un ranking en una sola línea.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 argumentos — list y int cumplen (devuelven [] y 0 respectivamente), así que los pasas directamente.
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ío — defaultdict(list) / defaultdict(set) encaja mejor.
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.
deque(maxlen=N), obtienes un buffer circular que descarta automáticamente el más antiguo cuando está lleno.| Método | Efecto | Coste |
|---|---|---|
| append(x) | Añade por la derecha | O(1) |
| appendleft(x) | Añade por la izquierda | O(1) |
| pop() | Elimina por la derecha | O(1) |
| popleft() | Elimina por la izquierda | O(1) |
| maxlen=N | Define longitud máxima (buffer circular) | El extremo opuesto se descarta automáticamente |
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.
_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.
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.
Verificación de conocimientos
Responde cada pregunta una a una.
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?