Imagina que defaultdict

 Imagina que defaultdict es como un "hotel infinito" 🏨

La analogía del hotel

Dict normal = Hotel tradicional

python
# 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

python
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:

python
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:

  1. Dict normal: "¡KeyError! No existe esa clave"

  2. defaultdict: "Veo que no existe... déjame crear un valor usando mi fábrica"

python
# 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

python
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

python
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

python
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 🎉

python
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 🗳️

python
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

  1. No es magia: Es un diccionario normal con un comportamiento extra

  2. La fábrica se llama CADA VEZ que se necesita un valor por defecto

  3. Solo funciona para acceso con [], no con .get()

  4. 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):

python
hotel_normal = {}
habitacion = hotel_normal['Ana']  # ¡Esto SÍ da KeyError inmediatamente!

✅ Correcto:

python
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']:

  1. Detecta que la clave no existe

  2. Llama inmediatamente a la "fábrica" que le diste

  3. Crea la entrada con esa clave y el valor por defecto

  4. Te devuelve el valor recién creado

python
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?"

  • : "No, pero deme una habitación igual"

  • Recepción: "¡Imposible! Necesita reserva previa" ❌

Hotel defaultdict:

  • Recepción: "Disculpe, ¿tiene reserva el señor Ana?"

  • : "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

python
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()

python
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

Entradas populares de este blog

¿Qué es un Closure?

Calculadora de edad

Funciones en Python: con y sin paréntesis