Imagina que defaultdict
Imagina que defaultdict es como un "hotel infinito" 🏨
La analogía del hotel
Dict normal = Hotel tradicional
# Hotel tradicional (dict normal)
hotel_normal = {}
# Si un huésped llega sin reserva:
try:
habitacion = hotel_normal['Ana'] # ¡Error! No tiene reserva
except KeyError:
print("¡No hay habitación para Ana!")defaultdict = Hotel con habitaciones infinitas
from collections import defaultdict
# Hotel con habitaciones infinitas (defaultdict)
hotel_infinito = defaultdict(lambda: "Habitación estándar")
# Huéspedes llegan sin reserva:
habitacion_ana = hotel_infinito['Ana'] # ¡Automáticamente le asigna "Habitación estándar"!
habitacion_carlos = hotel_infinito['Carlos'] # ¡Y otra habitación!
print(habitacion_ana) # "Habitación estándar"
print(hotel_infinito) # Muestra todas las asignaciones automáticas¿Cómo funciona realmente?
El "contrato de construcción" 🏗️
Cuando creas un defaultdict, le das una "fábrica" que sabe crear valores por defecto:
from collections import defaultdict
# Le damos diferentes "fábricas" de valores por defecto:
# Fábrica de listas vacías
fabrica_listas = defaultdict(list)
# Fábrica de ceros
fabrica_ceros = defaultdict(int)
# Fábrica personalizada
fabrica_personalizada = defaultdict(lambda: "VALOR POR DEFECTO")El proceso interno
Cuando intentas acceder a una clave que no existe:
Dict normal: "¡KeyError! No existe esa clave"
defaultdict: "Veo que no existe... déjame crear un valor usando mi fábrica"
# Internamente hace esto:
def __getitem__(self, key):
if key not in self:
self[key] = self.default_factory() # ¡Llama a la fábrica!
return self[key]Ejemplos detallados con la analogía
Ejemplo 1: El hotel de listas
from collections import defaultdict
# Hotel donde cada huésped recibe una LISTA VACÍA al llegar
hotel_listas = defaultdict(list)
# Huéspedes llegan y automáticamente reciben su lista
hotel_listas['Ana'].append('Toalla') # Ana: []
hotel_listas['Ana'].append('Jabón') # Ana ya tenía lista: ['Toalla']
hotel_listas['Carlos'].append('Champú') # Carlos: [] → ['Champú']
print(hotel_listas)
# defaultdict(<class 'list'>, {'Ana': ['Toalla', 'Jabón'], 'Carlos': ['Champú']})Ejemplo 2: El hotel contador
from collections import defaultdict
# Hotel que cuenta cuántas veces visita cada huésped
contador_visitas = defaultdict(int)
# Cada vez que un huésped llega, suma 1 a su contador
contador_visitas['Ana'] += 1 # Ana: 0 → 1
contador_visitas['Carlos'] += 1 # Carlos: 0 → 1
contador_visitas['Ana'] += 1 # Ana: 1 → 2
print(contador_visitas)
# defaultdict(<class 'int'>, {'Ana': 2, 'Carlos': 1})Ejemplo 3: Hotel con habitaciones personalizadas
from collections import defaultdict
def habitacion_de_lujo():
return {"cama": "king size", "vista": "mar", "precio": 200}
hotel_lujo = defaultdict(habitacion_de_lujo)
# Huéspedes recienen automáticamente habitación de lujo
print(hotel_lujo['Ana']) # {'cama': 'king size', 'vista': 'mar', 'precio': 200}
print(hotel_lujo['Carlos']) # {'cama': 'king size', 'vista': 'mar', 'precio': 200}Casos de la vida real
1. Organizando una fiesta 🎉
from collections import defaultdict
# Sin defaultdict (complicado)
fiesta_normal = {}
asistentes = [('Ana', 'postre'), ('Carlos', 'bebidas'), ('Ana', 'platos')]
for nombre, item in asistentes:
if nombre not in fiesta_normal:
fiesta_normal[nombre] = []
fiesta_normal[nombre].append(item)
# Con defaultdict (sencillo)
fiesta_facil = defaultdict(list)
for nombre, item in asistentes:
fiesta_facil[nombre].append(item) # ¡La lista se crea automáticamente!2. Contando votaciones 🗳️
from collections import defaultdict
votos = ['Ana', 'Carlos', 'Ana', 'María', 'Carlos', 'Ana']
conteo = defaultdict(int)
for voto in votos:
conteo[voto] += 1 # ¡El contador empieza en 0 automáticamente!
print(dict(conteo)) # {'Ana': 3, 'Carlos': 2, 'María': 1}Puntos clave para recordar
No es magia: Es un diccionario normal con un comportamiento extra
La fábrica se llama CADA VEZ que se necesita un valor por defecto
Solo funciona para acceso con
[], no con.get()Mantiene todas las funciones de un diccionario normal
¿Cuándo usarlo?
✅ Cuando agrupas elementos (listas por categoría)
✅ Cuando cuentas frecuencias
✅ Cuando quieres evitar verificaciones de
if clave in diccionario❌ Cuando el valor por defecto es costoso de crear
❌ Cuando quieres control explícito de errores
Corrección y aclaración
Tienes razón - me equivoqué en el ejemplo. Veamos la corrección:
❌ Incorrecto (como lo puse yo):
hotel_normal = {}
habitacion = hotel_normal['Ana'] # ¡Esto SÍ da KeyError inmediatamente!✅ Correcto:
from collections import defaultdict
# Dict NORMAL - SÍ da error
hotel_normal = {}
# habitacion = hotel_normal['Ana'] # ¡KeyError! No existe 'Ana'
# defaultdict - NO da error, CREA el valor
hotel_default = defaultdict(lambda: "Habitación estándar")
habitacion = hotel_default['Ana'] # ¡CREA 'Ana' con valor "Habitación estándar"!¿Qué hace realmente defaultdict?
Sí, crea el valor "en caliente" (on-the-fly) 🔥
Cuando haces defaultdict['clave_inexistente']:
Detecta que la clave no existe
Llama inmediatamente a la "fábrica" que le diste
Crea la entrada con esa clave y el valor por defecto
Te devuelve el valor recién creado
from collections import defaultdict
# Ejemplo real
contador = defaultdict(int)
print(contador) # defaultdict(<class 'int'>, {}) → VACÍO
# Acceso a clave inexistente
valor = contador['manzanas'] # ¡CREA 'manzanas' con valor 0!
print(valor) # 0
print(contador) # defaultdict(<class 'int'>, {'manzanas': 0}) → ¡YA EXISTE!Analogía corregida del hotel
Hotel tradicional (dict normal):
Recepción: "Disculpe, ¿tiene reserva el señor Ana?"
Tú: "No, pero deme una habitación igual"
Recepción: "¡Imposible! Necesita reserva previa" ❌
Hotel defaultdict:
Recepción: "Disculpe, ¿tiene reserva el señor Ana?"
Tú: "No, pero deme una habitación igual"
Recepción: "¡Claro! Le preparamos una habitación estándar al instante" ✅
Ejemplo paso a paso muy claro
from collections import defaultdict
# Creamos el defaultdict
mi_dict = defaultdict(list)
print("Estado inicial:", dict(mi_dict)) # {}
# Accedemos a una clave que NO EXISTE
print("\nAccediendo a 'frutas'...")
resultado = mi_dict['frutas'] # ¡CREA 'frutas' con valor []!
print("Resultado:", resultado) # []
print("Estado después:", dict(mi_dict)) # {'frutas': []}
# Ahora SÍ existe, podemos usarla normalmente
mi_dict['frutas'].append('manzana')
print("Después de agregar:", dict(mi_dict)) # {'frutas': ['manzana']}Diferencia crucial con .get()
from collections import defaultdict
dd = defaultdict(list)
normal_dict = {}
# defaultdict - CREA la entrada
resultado1 = dd['inexistente'] # ¡Crea 'inexistente' con valor []!
print(dd) # {'inexistente': []}
# .get() - NO crea la entrada (en ningún dict)
resultado2 = normal_dict.get('inexistente', []) # No crea, solo devuelve default
resultado3 = dd.get('otra_inexistente', []) # Tampoco crea en defaultdict
print(dd) # Sigue siendo {'inexistente': []} - no se creó 'otra_inexistente'¡Gracias por la corrección! Has identificado exactamente la magia de defaultdict: crear valores automáticamente cuando accedes a claves inexistente
Comentarios
Publicar un comentario