QuieroProgramar por Rodri Gonzalez
Python · Lección 15 de 27

Anidación de estructuras

En el mundo real los datos no vienen planos: vienen como listas dentro de listas, diccionarios dentro de diccionarios, listas de diccionarios con listas adentro. Esta lección te enseña a construir, navegar y transformar cualquier estructura anidada — la habilidad que necesitás para trabajar con JSON y pandas.

90–120 min Prerrequisitos: 08 Listas, 10 Diccionarios, 14 Métodos de dicts
01

Concepto teórico

¿Qué es la anidación?

Anidar significa poner una estructura de datos dentro de otra. Python te permite combinar listas, tuplas, diccionarios y sets en cualquier profundidad. Los cuatro patrones de anidación que vas a usar constantemente son:

  1. Lista de listas — una matriz o tabla: [[1,2],[3,4],[5,6]]
  2. Lista de diccionarios — registros/filas de una tabla: [{"nombre": "García", "score": 720}, ...]
  3. Diccionario de diccionarios — datos jerárquicos / JSON: {"cliente": {"nombre": "García", "scoring": {"valor": 720}}}
  4. Diccionario de listas — columnas de una tabla: {"nombre": ["García","López"], "score": [720,680]}
¿Por qué esto importa tanto? Porque un DataFrame de pandas es internamente un diccionario de listas (patrón 4). Cuando hacés pd.DataFrame(data), data puede ser cualquiera de estos 4 patrones. Y cuando consumís una API REST, la respuesta JSON es siempre una combinación de patrones 2 y 3. Dominar anidación = dominar el formato de TODOS tus datos.

Lista de listas — la "matriz"

Una lista de listas representa una tabla bidimensional, como una hoja de cálculo. Cada sublista es una "fila":

# Cada fila: [nombre, edad, score, saldo]
tabla = [
    ["García",    35, 720, 150000],
    ["López",     42, 680, 320000],
    ["Martínez",  28, 590,  45000],
]
# Acceso: tabla[fila][columna]
print(tabla[0][0])   # "García"  (fila 0, columna 0)
print(tabla[1][3])   # 320000    (fila 1, columna 3)
print(tabla[2][2])   # 590       (fila 2, columna 2)

El problema de las listas de listas es que tenés que recordar qué posición es cada campo. tabla[1][3] no dice nada — ¿es el saldo? ¿la edad? Por eso, para datos con campos nombrados, se prefiere la lista de diccionarios.

Lista de diccionarios — la "base de datos en memoria"

Este es el patrón más importante de todo el curso. Cada diccionario es un registro (fila) con campos nombrados (columnas):

clientes = [
    {"nombre": "García",   "ciudad": "BsAs",    "score": 720, "saldo": 150000},
    {"nombre": "López",    "ciudad": "Córdoba",  "score": 680, "saldo": 320000},
    {"nombre": "Martínez", "ciudad": "Rosario",  "score": 590, "saldo":  45000},
]
# Acceso claro y descriptivo:
print(clientes[0]["nombre"])  # "García"
print(clientes[1]["saldo"])   # 320000
Analogía exacta: una lista de diccionarios es literalmente una tabla de base de datos. Cada diccionario es una fila, cada clave es un nombre de columna. Cuando hagas pd.DataFrame(clientes), pandas la convierte en una tabla perfecta automáticamente. Y cuando hagas df.to_dict("records"), obtenés de vuelta esta misma estructura.

Diccionario de diccionarios — datos jerárquicos (JSON)

Cuando los datos tienen estructura jerárquica (un cliente tiene scoring, el scoring tiene subcampos), usás diccionarios dentro de diccionarios:

cliente = {
    "id": 12345,
    "nombre": "García",
    "scoring": {
        "valor": 720,
        "fecha": "2025-01-15",
        "entidad": "BCRA"
    },
    "cuentas": [
        {"tipo": "CA$", "saldo": 150000},
        {"tipo": "CA USD", "saldo": 5000}
    ]
}
# Acceso encadenado:
print(cliente["scoring"]["valor"])      # 720
print(cliente["cuentas"][0]["saldo"])    # 150000
Peligro de acceso directo: si alguna clave intermedia no existe, toda la cadena explota con KeyError. Para acceso seguro, usá .get() encadenado: cliente.get("scoring", {}).get("valor", 0). Si "scoring" no existe, el {} evita que el segundo .get() falle.

Diccionario de listas — formato columnar

Cada clave es una columna y su valor es la lista de datos de esa columna. Este es el formato que pandas usa internamente:

datos_columnar = {
    "nombre": ["García", "López", "Martínez"],
    "score":  [720, 680, 590],
    "saldo":  [150000, 320000, 45000],
}
# pd.DataFrame(datos_columnar) → tabla perfecta

Patrones de recorrido profundo

Para recorrer estructuras anidadas necesitás for anidados. La regla es simple: un for por cada nivel de anidación:

Tip profesional: si te encontrás con más de 3 niveles de for anidados, es señal de que necesitás refactorizar. Extraé el loop interno a una función con nombre descriptivo. Esto mejora la legibilidad enormemente.
En el trabajo real: las APIs de bancos, fintechs y servicios financieros devuelven JSON con 4-5 niveles de anidación. Saber navegar estas estructuras con .get() encadenado, list comprehensions sobre sublistas, y extracciones selectivas es una habilidad que usás todos los días como analista.
02

Ejemplos explicados paso a paso

Ejemplo 1: Lista de listas — operaciones sobre tabla

ejemplo_01_matriz.pyPython

        
Hacé clic en ▶ Ejecutar

Ejemplo 2: Lista de diccionarios — la estructura estrella

ejemplo_02_lista_dicts.pyPython

        
Hacé clic en ▶ Ejecutar

Ejemplo 3: Diccionario anidado — navegando JSON

ejemplo_03_json.pyPython

        
Hacé clic en ▶ Ejecutar

Ejemplo 4: Construir estructura anidada desde datos planos

ejemplo_04_construir.pyPython

        
Hacé clic en ▶ Ejecutar

Ejemplo 5: Comprehensions sobre estructuras anidadas

ejemplo_05_comprehensions.pyPython

        
Hacé clic en ▶ Ejecutar
03

Referencia rápida

Patrón Estructura Acceso Uso principal
Lista de listas [[1,2],[3,4]] m[fila][col] Matrices, tablas sin encabezado
Lista de dicts [{"k":v}, ...] lista[i]["clave"] Registros/filas de tabla (el más usado)
Dict de dicts {"a": {"b": v}} d["a"]["b"] JSON, datos jerárquicos
Dict de listas {"col": [v1,v2]} d["col"][i] Formato columnar (pandas interno)
Operación Sintaxis
Extraer columna de lista de dicts [d["campo"] for d in lista]
Filtrar registros [d for d in lista if d["campo"] > x]
Aplanar lista de listas [x for sub in lista for x in sub]
Aplanar sublistas de dicts [item for d in lista for item in d["sublista"]]
Acceso seguro profundo d.get("a", {}).get("b", default)
Convertir a DataFrame pd.DataFrame(lista_de_dicts)
04

Ejercicios

Nivel 1 · Básico

Ejercicio 1: Acceso a lista de listas

Dada tabla = [["GGAL",5200],["YPF",28000],["PAMP",3100]], imprimí el nombre y precio de cada acción. Debe incluir 28000.

ejercicio_01.pyDebe incluir "28000"

          
Hacé clic en ▶ Ejecutar
Nivel 1 · Básico

Ejercicio 2: Acceso a lista de dicts

Dada clientes = [{"nombre":"García","score":720},{"nombre":"López","score":680}], imprimí cada nombre con su score. Debe incluir García.

ejercicio_02.pyDebe incluir "García"

          
Hacé clic en ▶ Ejecutar
Nivel 1 · Básico

Ejercicio 3: Acceso a dict anidado

Dado data = {"cliente": {"nombre": "Pérez", "scoring": {"valor": 810}}}, imprimí el nombre y el valor del scoring accediendo con claves encadenadas. Debe incluir 810.

ejercicio_03.pyDebe incluir "810"

          
Hacé clic en ▶ Ejecutar
Nivel 2 · Intermedio

Ejercicio 4: Extraer columna de lista de dicts

De la lista de clientes, extraé todos los saldos en una lista usando comprehension. Calculá el total. Debe incluir 1477000.

ejercicio_04.pyDebe incluir "1477000"

          
Hacé clic en ▶ Ejecutar
Nivel 2 · Intermedio

Ejercicio 5: Filtrar lista de dicts con comprehension

De la misma lista, filtrá los clientes con saldo > $100K e imprimí sus nombres. Debe incluir Pérez.

ejercicio_05.pyDebe incluir "Pérez"

          
Hacé clic en ▶ Ejecutar
Nivel 2 · Intermedio

Ejercicio 6: Aplanar lista de listas

Dada cuentas = [[150000, 500000], [320000], [890000, 5000, 12000]], aplanalas en una sola lista y calculá el total. Debe incluir 1877000.

ejercicio_06.pyDebe incluir "1877000"

          
Hacé clic en ▶ Ejecutar
Nivel 3 · Avanzado

Ejercicio 7: Acceso seguro con .get() encadenado

Dado un dict donde algunos campos pueden no existir, accedé de forma segura al scoring, email y teléfono con defaults. Debe incluir No informado.

ejercicio_07.pyDebe incluir "No informado"

          
Hacé clic en ▶ Ejecutar
Nivel 3 · Avanzado

Ejercicio 8: Construir lista de dicts desde datos planos

Dados nombres = ["García","López","Pérez"], scores = [720,680,810], saldos = [150000,320000,890000], construí una lista de diccionarios con campos nombre, score, saldo. Debe incluir 810.

ejercicio_08.pyDebe incluir "810"

          
Hacé clic en ▶ Ejecutar
Nivel 3 · Avanzado

Ejercicio 9: Comprehension sobre sublistas de dicts

Dados clientes con sublista de cuentas, extraé TODAS las cuentas como tuplas (nombre_cliente, tipo_cuenta, saldo) con una comprehension anidada. Debe incluir CA$.

ejercicio_09.pyDebe incluir "CA$"

          
Hacé clic en ▶ Ejecutar
Nivel 4 · Desafío

Ejercicio 10: Reporte completo desde API simulada

Procesá esta respuesta de API simulada: extraé el nombre del cliente, su score, listá todas las cuentas con su saldo, calculá el saldo total, y verificá si el uso de tarjeta supera el 80% del límite. Debe incluir REPORTE.

ejercicio_10_desafio.pyDebe incluir "REPORTE"

          
Hacé clic en ▶ Ejecutar
05

Resumen y conexión

En la siguiente lección (16 · While) vas a aprender el primer bucle con condición: cómo repetir código mientras se cumpla una condición, simulaciones financieras mes a mes, y patrones de validación de input.

Recursos: Nested List Comprehensions · JSON module