Diccionarios Anidados
Aprende a crear, acceder, modificar, eliminar e iterar diccionarios anidados en Python con ejemplos claros y buenas prácticas.
Un diccionario anidado es un diccionario que contiene otros diccionarios como valores. Esto crea una estructura de datos jerárquica (en forma de árbol) ideal para representar datos agrupados del mundo real, como una colección de perfiles de usuario, un archivo de configuración o una respuesta de una API JSON.
Este capítulo cubre:
- Crear diccionarios anidados
- Acceder a valores a cualquier profundidad
- Modificar y agregar entradas
- Eliminar claves y sub-diccionarios
- Iterar sobre diccionarios anidados
- Usar
.get()para evitarKeyError - Trabajar con datos profundamente anidados y JSON
Crear Diccionarios Anidados
Define un diccionario cuyos valores son a su vez diccionarios:
people = {
"person1": {"name": "Alice", "age": 30},
"person2": {"name": "Bob", "age": 25},
}
print(people)
# {'person1': {'name': 'Alice', 'age': 30}, 'person2': {'name': 'Bob', 'age': 25}}También puedes construir un diccionario anidado de forma incremental:
people = {}
people["person1"] = {"name": "Alice", "age": 30}
people["person2"] = {"name": "Bob", "age": 25}
print(people["person1"]) # {'name': 'Alice', 'age': 30}No hay límite de profundidad: un valor interno puede ser a su vez un diccionario anidado.
Acceder a Valores en Diccionarios Anidados
Encadena accesos con corchetes para profundizar en cualquier nivel:
Acceso Seguro con .get()
Los accesos encadenados con corchetes lanzan un KeyError si falta una clave. Usa .get() para devolver un valor predeterminado en su lugar:
people = {
"person1": {"name": "Alice", "age": 30},
}
# Safe: returns None if "person3" does not exist
print(people.get("person3", {}).get("name", "Unknown")) # Unknown
# Risky: raises KeyError
# print(people["person3"]["name"])El patrón .get(outer_key, {}).get(inner_key, default) es la forma idiomática de leer datos anidados opcionales sin un bloque try/except.
Modificar Valores en Diccionarios Anidados
Asigna directamente a través de la cadena de claves:
Agregar Nuevos Sub-Diccionarios
Asigna un nuevo literal de diccionario a una nueva clave externa:
Usar setdefault() para Agregar Solo si No Existe
setdefault() inserta una clave con un valor predeterminado únicamente si no existe todavía, lo que resulta útil al construir diccionarios anidados a partir de un flujo de datos:
scores = {}
for student, subject, grade in [
("Alice", "math", 90),
("Alice", "english", 85),
("Bob", "math", 78),
]:
scores.setdefault(student, {})[subject] = grade
print(scores)
# {'Alice': {'math': 90, 'english': 85}, 'Bob': {'math': 78}}Eliminar Claves y Sub-Diccionarios
Usa del para eliminar una clave (y su valor) en cualquier nivel:
people = {
"person1": {"name": "Alice", "age": 30},
"person2": {"name": "Bob", "age": 25},
"person3": {"name": "Carol", "age": 40},
}
del people["person2"]["age"] # remove one key from an inner dict
del people["person3"] # remove an entire sub-dictionary
print(people)
# {'person1': {'name': 'Alice', 'age': 30}, 'person2': {'name': 'Bob'}}Usa .pop() si también necesitas el valor eliminado:
removed = people["person1"].pop("age", None)
print(removed) # 30
print(people["person1"]) # {'name': 'Alice'}Iterar sobre Diccionarios Anidados
Recorrer claves externas y elementos internos
people = {
"person1": {"name": "Alice", "age": 30},
"person2": {"name": "Bob", "age": 25},
}
for person_id, details in people.items():
print(f"{person_id}:")
for key, value in details.items():
print(f" {key}: {value}")Salida:
person1:
name: Alice
age: 30
person2:
name: Bob
age: 25Aplanar un diccionario anidado en una lista de registros
Un patrón habitual al preparar datos para su procesamiento:
people = {
"person1": {"name": "Alice", "age": 30},
"person2": {"name": "Bob", "age": 25},
}
records = [
{"id": pid, **info}
for pid, info in people.items()
]
print(records)
# [{'id': 'person1', 'name': 'Alice', 'age': 30},
# {'id': 'person2', 'name': 'Bob', 'age': 25}]Diccionarios Profundamente Anidados
Python no impone ningún límite a la profundidad de anidamiento. A continuación se muestra un ejemplo de tres niveles que representa la estructura de departamentos de una empresa:
company = {
"engineering": {
"frontend": {
"lead": "Alice",
"headcount": 5,
},
"backend": {
"lead": "Bob",
"headcount": 8,
},
},
"marketing": {
"content": {
"lead": "Carol",
"headcount": 3,
},
},
}
# Access three levels deep
print(company["engineering"]["backend"]["lead"]) # Bob
# Iterate two levels and collect leads
leads = [
dept_data["lead"]
for dept_data in (
team
for teams in company.values()
for team in teams.values()
)
]
print(leads) # ['Alice', 'Bob', 'Carol']Cuando se leen claves profundas que pueden estar ausentes, encadena llamadas a .get():
lead = company.get("hr", {}).get("recruitment", {}).get("lead", "Not assigned")
print(lead) # Not assignedDiccionarios Anidados y JSON
Los objetos JSON se corresponden directamente con diccionarios anidados de Python. El módulo json convierte entre ambos:
import json
data = {
"users": {
"u1": {"name": "Alice", "active": True},
"u2": {"name": "Bob", "active": False},
}
}
# Serialize to JSON string
json_str = json.dumps(data, indent=2)
print(json_str)
# Deserialize back to a nested dict
restored = json.loads(json_str)
print(restored["users"]["u1"]["name"]) # AliceEste es el flujo de trabajo habitual al consumir REST APIs o leer archivos de configuración.
Errores Comunes
Trampa de la referencia compartida. Si copias un diccionario interno por referencia y luego lo modificas, tanto el original como la copia cambian:
original = {"a": {"x": 1}}
shallow = original.copy() # copies only the outer dict
shallow["a"]["x"] = 99
print(original["a"]["x"]) # 99 ← original is affected!Usa copy.deepcopy() cuando necesites una copia completamente independiente:
import copy
original = {"a": {"x": 1}}
deep = copy.deepcopy(original)
deep["a"]["x"] = 99
print(original["a"]["x"]) # 1 ← original is safeConsulta el capítulo Copiar Diccionarios para una comparación completa entre copia superficial y copia profunda.
KeyError por claves intermedias inexistentes. Escribir d["a"]["b"] = 1 lanza un KeyError si "a" aún no existe. Usa setdefault o collections.defaultdict para crear niveles intermedios automáticamente.
Referencia Rápida
| Tarea | Sintaxis |
|---|---|
| Acceder a un valor interno | d["outer"]["inner"] |
| Acceso seguro | d.get("outer", {}).get("inner", default) |
| Agregar / actualizar clave interna | d["outer"]["inner"] = value |
| Agregar nuevo sub-diccionario | d["new_key"] = {...} |
| Eliminar clave interna | del d["outer"]["inner"] |
| Eliminar sub-diccionario | del d["outer"] |
| Iterar todas las entradas | for k, v in d.items(): for ik, iv in v.items(): |
| Copia profunda | import copy; copy.deepcopy(d) |