W3docs

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 evitar KeyError
  • 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:

python— editable, runs on the server

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:

python— editable, runs on the server

Agregar Nuevos Sub-Diccionarios

Asigna un nuevo literal de diccionario a una nueva clave externa:

python— editable, runs on the server

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

Aplanar 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 assigned

Diccionarios 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"])  # Alice

Este 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 safe

Consulta 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

TareaSintaxis
Acceder a un valor internod["outer"]["inner"]
Acceso segurod.get("outer", {}).get("inner", default)
Agregar / actualizar clave internad["outer"]["inner"] = value
Agregar nuevo sub-diccionariod["new_key"] = {...}
Eliminar clave internadel d["outer"]["inner"]
Eliminar sub-diccionariodel d["outer"]
Iterar todas las entradasfor k, v in d.items(): for ik, iv in v.items():
Copia profundaimport copy; copy.deepcopy(d)

Práctica

Práctica
Which method safely reads a missing key from a nested dict without raising a KeyError?
Which method safely reads a missing key from a nested dict without raising a KeyError?
Was this page helpful?