defaultdict
: La evolución de un diccionario sin condiciones
Python cuenta con innumerables bibliotecas externas, pero entender bien la biblioteca estándar es suficiente para escribir código potente en el trabajo. En este artículo, profundizaremos en collections.defaultdict
.
A través de este artículo, no solo te presentaré el concepto, sino que también entenderás claramente cuándo, por qué y cómo usar defaultdict
.
Si tienes curiosidad sobre la primera clase de la serie collections
, el Counter
, te recomiendo que leas el artículo anterior.
Conquista de la biblioteca estándar de Python ① - collections.Counter
1. Concepto Básico: ¿Qué es defaultdict
?
defaultdict
es una subclase de diccionario (dict) especial incluida en el módulo de la biblioteca estándar de Python collections
. Mientras que en un diccionario normal, al acceder a una clave inexistente se produce un KeyError
, en defaultdict
se puede especificar una función de creación de valores por defecto, lo que hace que el código sea mucho más limpio y ayuda a prevenir errores.
2. Uso Básico
from collections import defaultdict
d = defaultdict(int)
d['apple'] += 1
print(d) # defaultdict(<class 'int'>, {'apple': 1})
Aquí, int()
devuelve 0
como valor por defecto. Al acceder a la clave inexistente 'apple'
, se genera automáticamente 0
sin KeyError
, y luego se realiza +1
.
3. Ejemplos de Valores por Defecto Variados
from collections import defaultdict
# Valor por defecto: 0 (int)
counter = defaultdict(int)
counter['a'] += 1
print(counter) # defaultdict(<class 'int'>, {'a': 1})
# Valor por defecto: lista vacía
group = defaultdict(list)
group['fruit'].append('apple')
group['fruit'].append('banana')
print(group) # defaultdict(<class 'list'>, {'fruit': ['apple', 'banana']})
# Valor por defecto: conjunto vacío
unique_tags = defaultdict(set)
unique_tags['tags'].add('python')
unique_tags['tags'].add('coding')
print(unique_tags) # defaultdict(<class 'set'>, {'tags': {'python', 'coding'}})
# Valor por defecto: valor inicial personalizado
fixed = defaultdict(lambda: 100)
print(fixed['unknown']) # 100
4. Ejemplos en la Práctica
1. Contar la Frecuencia de Palabras
words = ['apple', 'banana', 'apple', 'orange', 'banana']
counter = defaultdict(int)
for word in words:
counter[word] += 1
print(counter)
# defaultdict(<class 'int'>, {'apple': 2, 'banana': 2, 'orange': 1})
👉 Counter vs defaultdict
El conteo de la frecuencia de palabras es más especializado en collections.Counter()
, así que si necesitas análisis estadísticos o de ranking, es recomendable usar Counter
. Sin embargo, para conteos acumulativos simples, defaultdict(int)
también se puede usar con suficiente simplicidad.
2. Organizar Registros por Grupo
logs = [
('2024-01-01', 'INFO'),
('2024-01-01', 'ERROR'),
('2024-01-02', 'DEBUG'),
]
grouped = defaultdict(list)
for date, level in logs:
grouped[date].append(level)
print(grouped)
# defaultdict(<class 'list'>, {'2024-01-01': ['INFO', 'ERROR'], '2024-01-02': ['DEBUG']})
3. Organizar Etiquetas Sin Duplicados
entries = [
('post1', 'python'),
('post1', 'coding'),
('post1', 'python'),
]
tags = defaultdict(set)
for post, tag in entries:
tags[post].add(tag)
print(tags)
# defaultdict(<class 'set'>, {'post1': {'python', 'coding'}})
5. Precauciones
defaultdict
almacena internamente un generador de valores por defecto, por lo que al usarrepr()
puede verse diferente a undict
normal.- Puede haber problemas al serializar a JSON. Es seguro convertirlo a
dict(d)
antes de procesarlo. - Los valores por defecto solo se crean al acceder con
[]
. Si usasget()
, no se crearán.
from collections import defaultdict
d = defaultdict(list)
print(d.get('missing')) # None
print(d['missing']) # []
6. ¿Cuándo es Bueno Usarlo? – 3 Ventajas Decisivas de defaultdict
defaultdict
eleva simultáneamente la legibilidad, mantenibilidad y seguridad en situaciones que suelen emplear el patrón dict
+ condiciones
. Especialmente en las siguientes 3 situaciones es donde se siente '¡Ah, definitivamente es mejor usar defaultdict!'
6-1. Código de Agregación que Cuenta/Suma Sin Condicionales
from collections import defaultdict
# dict normal
counts = {}
for item in items:
if item not in counts:
counts[item] = 0
counts[item] += 1
# defaultdict
counts = defaultdict(int)
for item in items:
counts[item] += 1
✔ Al eliminar las condiciones, el código se simplifica y se reduce el riesgo de errores.
✔ Especialmente adecuado para análisis de registros, conteo de palabras y procesamiento de grandes volúmenes de datos.
6-2. Acumulando Listas/Conjuntos Como Sustituto de setdefault
from collections import defaultdict
# dict normal
posts = {}
for tag, post in data:
if tag not in posts:
posts[tag] = []
posts[tag].append(post)
# defaultdict
posts = defaultdict(list)
for tag, post in data:
posts[tag].append(post)
✔ Es mucho más intuitivo que setdefault()
y más claro dentro de los bucles.
✔ Estructura optimizada para agrupación de datos.
6-3. Automatizar la Inicialización al Construir un Diccionario Anidado
# Diccionario normal
matrix = {}
if 'x' not in matrix:
matrix['x'] = {}
matrix['x']['y'] = 10
# Anidado con defaultdict
matrix = defaultdict(lambda: defaultdict(int)) # Se genera automáticamente defaultdict(int) al faltar la clave
matrix['x']['y'] += 10
✔ Facilita la creación de estructuras de datos anidadas, lo que es muy útil para trabajar con diccionarios multidimensionales.
✔ Tiene grandes ventajas en minería de datos, análisis y almacenamiento en estructuras de árbol.
lambda: defaultdict(int)
es una estructura que devuelvedefaultdict(int)
cada vez que falta una clave, creando automáticamente el diccionario anidado.
7. Resumen
collections.defaultdict
puede parecer una simple extensión de dict
para los principiantes, pero con su uso se siente que es una herramienta estructural que hace que el código sea más claro y seguro.
- Se puede utilizar un diccionario sin preocuparse por
KeyError
. - Se puede agrupar y acumular datos sin condiciones.
- Se puede construir diccionarios anidados de manera intuitiva.
# Ejemplo manejado de manera estable y concisa con defaultdict
salaries = defaultdict(int)
for dept, amount in records:
salaries[dept] += amount
Si con una línea de código se puede prevenir errores, mejorar la legibilidad y aumentar la mantenibilidad,
se puede considerar que defaultdict
no es solo una función conveniente, sino que es una herramienta clave en la forma de pensar de Python.
El siguiente tema que abordaremos será pathlib
.
Una forma moderna de manejar archivos/directorios de manera orientada a objetos, útil para principiantes y niveles intermedios. Los desarrolladores que están acostumbrados a os.path
sentirán '¡Wow! ¡Es mucho más simple que os.path!'. Espera con ansias el próximo artículo.
Add a New Comment