📘 Parte 1 — Pares clave-valor
Un diccionario (dict
) es una estructura de datos que almacena información en
pares clave-valor. Cada clave identifica un valor. Es perfecto para modelar
objetos del mundo real: usuarios, productos, configuraciones, etc.
✅ Sintaxis básica
# Diccionario literal con llaves
usuario = {
"nombre": "Javier",
"edad": 25,
"activo": True
}
print(usuario["nombre"]) # >> Javier
Lee la idea así: “la clave nombre
apunta al valor Javier
”.
🧩 ¿Qué puede ser una clave?
- Debe ser hashable (inmutable):
str
,int
,float
,bool
,tuple
(si sus elementos son hashables)… - No pueden ser claves tipos mutables:
list
,dict
,set
. - Las claves son únicas. Una clave repetida sobrescribe el valor anterior.
ok = { ("ES","MAD"): "Madrid", ("ES","SEV"): "Sevilla" } # tuplas como clave
bad = { ["ES","MAD"]: "Madrid" } # ❌ TypeError: unhashable type: 'list'
(año, mes)
).📦 Crear diccionarios — formas comunes
Método | Ejemplo | Notas |
---|---|---|
Literal | {"a":1, "b":2} | La forma más clara |
Constructor dict() | dict(a=1, b=2) | Solo para claves válidas como identificadores |
Desde pares | dict([("a",1), ("b",2)]) | Útil al transformar listas/tuplas |
Comprehension | {x: x*x for x in range(3)} | Potente y expresivo |
fromkeys | dict.fromkeys(["a","b"], 0) | Inicializa varias claves con un valor |
# Ejemplos rápidos
d1 = {"user": "javi", "rol": "admin"}
d2 = dict(user="javi", rol="admin")
d3 = dict([("user","javi"), ("rol","admin")])
d4 = {k: len(k) for k in ("manzana","pera","uva")}
d5 = dict.fromkeys(["id","estado","total"], None)
🗂️ Valores: cualquier objeto
Un valor puede ser de cualquier tipo: números, cadenas, listas, otros diccionarios, funciones, etc.
producto = {
"sku": 101,
"nombre": "Teclado",
"precio": 29.9,
"tags": ["periférico", "usb"],
"stock": {"MAD": 12, "BCN": 8}
}
🧠 Propiedades importantes
- 📥 Orden de inserción preservado (Python 3.7+): al iterar verás las claves en el orden en que se añadieron.
- ⚡ Acceso por clave en tiempo promedio O(1): muy rápido para búsquedas.
- 🔐 Unicidad de clave: asignar de nuevo la misma clave sobrescribe el valor.
d = {"a":1, "b":2, "a":99}
print(d) # {'a': 99, 'b': 2} ← 'a' se sobrescribió
🔎 Acceso seguro vs. acceso directo
Acceso directo con []
lanza error si la clave no existe. Con get()
puedes definir un valor por defecto.
usuario = {"nombre": "Javi"}
# Acceso directo (puede fallar)
# print(usuario["edad"]) # ❌ KeyError
# Acceso seguro con .get()
print(usuario.get("edad")) # None
print(usuario.get("edad", "N/A")) # N/A
🎯 Casos de uso típicos (clave-valor)
1) Perfil de usuario
perfil = {"usuario":"jcachon", "email":"javi@example.com", "premium": True}
print(perfil["usuario"])
2) Índices y catálogos
precios = {"pan": 1.1, "leche": 1.0, "queso": 4.5}
print(precios["queso"])
3) Traducciones / mapeos
es_en = {"hola":"hello", "adios":"goodbye"}
print(es_en.get("hola","(missing)"))
📋 Tabla rápida — operaciones básicas (vista previa)
Tarea | Ejemplo | Qué hace |
---|---|---|
Acceder | d["k"] / d.get("k","def") | Obtiene el valor (directo/seguro) |
Insertar/Actualizar | d["k"] = v | Crea o sobrescribe |
Comprobar clave | "k" in d | Devuelve True/False |
Tamaño | len(d) | Cuenta claves |
Vaciar | d.clear() | Elimina todo |
🔜 En la Parte 2 verás modificación y eliminación en detalle: update()
,
del
, pop()
, popitem()
…
⚠️ Errores comunes (y cómo evitarlos)
- ❌ Usar tipos mutables como clave (
list
,dict
): “unhashable type”.
✅ Solución: usa tuplas u otro tipo inmutable. - ❌ Dar por hecho que la clave existe y usar
[]
.
✅ Solución: usa.get()
con valor por defecto si no estás seguro. - ❌ Repetir claves en el literal y creer que se “guardan ambas”.
✅ Solución: recuerda que la última asignación gana.
🧪 Mini-ejercicios
- Crea un
dict
persona
connombre
,edad
,ciudad
. Imprimepersona["nombre"]
. - Usa
.get()
para obtenertelefono
con valor por defecto"N/D"
. - Crea un mapa de precios (
producto → precio
) y consulta el de"manzana"
. - Usa una tupla como clave compuesta para guardar la temperatura de
("Madrid","Enero")
. - Escribe una comprensión de diccionario que mapee
n
an*n
paran
en 1..5.
🏁 Cierre de la Parte 1
Has entendido el corazón de los diccionarios: los pares clave-valor.
En la Parte 2 veremos cómo acceder, modificar y eliminar de forma segura y profesional,
con patrones que evitarán errores en producción.
🛠️ Parte 2 — Acceso, modificación y eliminación
Ahora que ya dominas la idea de clave → valor, toca operar un diccionario como un pro:
acceder de forma segura, modificar sin sorpresas y eliminar con intención clara.
Verás atajos útiles como get()
, setdefault()
, update()
y los operadores de fusión |
y |=
(Python ≥ 3.9).
🔎 Acceso a valores
Método | Ejemplo | Comportamiento |
---|---|---|
Acceso directo | usuario["email"] | Devuelve el valor o lanza KeyError si no existe |
Acceso seguro | usuario.get("email") | Devuelve el valor o None (o un defecto) si no existe |
Acceso con valor por defecto | usuario.get("email", "N/D") | Evita excepciones y documenta intención |
Crear si falta | usuario.setdefault("email", "N/D") | Devuelve el valor; si no existe, lo crea con el defecto |
usuario = {"nombre": "Javi"}
# Acceso directo (riesgo KeyError):
# print(usuario["email"])
print(usuario.get("email")) # None
print(usuario.get("email", "N/D")) # N/D
print(usuario.setdefault("email", "N/D")) # crea 'email' si no existía
print(usuario) # {'nombre': 'Javi', 'email': 'N/D'}
.get()
. Si además quieres crear la clave cuando falte, usa .setdefault()
.✍️ Inserción y actualización
Asignación simple
perfil = {"user": "jcachon"}
perfil["rol"] = "admin" # inserta si no existe
perfil["rol"] = "editor" # sobrescribe si existe
print(perfil) # {'user':'jcachon','rol':'editor'}
Actualizar varios campos: update()
perfil.update({"activo": True, "email": "javi@example.com"})
# También acepta pares clave-valor o keywords:
perfil.update([("pais","ES")], ciudad="Madrid")
print(perfil)
Fusión de diccionarios (Python ≥ 3.9): |
y |=
a = {"a":1, "b":2}
b = {"b":99, "c":3}
c = a | b # nuevo dict (a no cambia)
print(c) # {'a':1,'b':99,'c':3}
a |= b # fusiona en 'a' (in place)
print(a) # {'a':1,'b':99,'c':3}
Contadores y acumulación: get()
vs setdefault()
texto = "banana"
freq = {}
for ch in texto:
freq[ch] = freq.get(ch, 0) + 1 # patrón clásico con get()
# Alternativa con setdefault():
freq2 = {}
for ch in texto:
freq2.setdefault(ch, 0)
freq2[ch] += 1
print(freq, freq2)
🧹 Eliminación de elementos
Método | Ejemplo | Qué devuelve | Notas |
---|---|---|---|
del | del d["k"] | — | Lanza KeyError si no existe |
pop() | d.pop("k") | Valor eliminado | Con segundo arg, devuelve ese defecto si no existe |
popitem() | d.popitem() | Par (clave, valor) | Elimina el último par (LIFO en 3.7+) |
clear() | d.clear() | — | Vacía el diccionario |
d = {"a":1, "b":2, "c":3}
# del
del d["b"]
# pop con valor por defecto (evita KeyError)
valor = d.pop("x", None) # None si no existe 'x'
# popitem: quita el último par
clave, val = d.popitem()
# clear: vacía todo
d.clear()
print(d) # {}
🧠 Acceso a estructuras anidadas (y creación segura)
Con diccionarios anidados es común el patrón “crear si falta” con setdefault()
.
inventario = {}
# Queremos sumar stock en inventario["MAD"]["SKU101"]
ciudad = "MAD"
sku = "SKU101"
cantidad = 5
inventario.setdefault(ciudad, {}) # crea dict para ciudad si no existe
inventario[ciudad][sku] = inventario[ciudad].get(sku, 0) + cantidad
print(inventario) # {'MAD': {'SKU101': 5}}
Para lecturas anidadas que podrían fallar, combina get()
:
ciudades = {"MAD": {"poblacion": 3_200_000}}
poblacion = ciudades.get("BCN", {}).get("poblacion", "N/D")
print(poblacion) # N/D
⚠️ Pitfalls (y cómo evitarlos)
- ❌ Modificar mientras iteras: puede causar errores lógicos.
d = {"a":1,"b":2,"c":3} for k in list(d.keys()): # itera sobre copia if d[k] % 2 == 1: del d[k] print(d) # {'b': 2}
- ❌ Asumir que
popitem()
quita “el primero”: desde 3.7+ es LIFO (el último insertado). - ❌ Reconstruir diccionarios grandes en bucles críticos (p. ej.,
a = a | b
dentro de un for).
✅ Precalcula y fusiona una sola vez. - ❌ Usar acceso directo cuando la clave puede faltar.
✅.get()
o try/exceptKeyError
si necesitas distinguir casos.
🧩 Patrones prácticos
1) “Upsert” (insertar o actualizar)
def upsert(d, key, fn_compute):
d[key] = fn_compute(d.get(key))
return d[key]
precios = {"pan": 1.1}
upsert(precios, "pan", lambda v: (v or 0) + 0.2) # actualiza
upsert(precios, "leche", lambda v: (v or 0) + 1) # inserta
print(precios) # {'pan': 1.3, 'leche': 1.0}
2) Normalización “crear si falta”
registro = {}
for linea in ["ES;MAD", "ES;BCN", "PT;LIS"]:
pais, ciudad = linea.split(";")
registro.setdefault(pais, []).append(ciudad)
print(registro) # {'ES':['MAD','BCN'],'PT':['LIS']}
3) Valores por defecto con setdefault
(config)
cfg = {"db": {"host": "localhost"}}
cfg.setdefault("db", {}).setdefault("port", 5432)
print(cfg) # {'db': {'host':'localhost','port':5432}}
📋 Chuleta rápida
Necesito… | Usa | Ejemplo |
---|---|---|
Leer sin error si falta | get() | d.get("k","N/D") |
Leer y crear si falta | setdefault() | d.setdefault("k", []) |
Actualizar varios campos | update() | d.update({...}) |
Fusionar diccionarios | | / |= | a |= b |
Eliminar y obtener valor | pop() | d.pop("k", defecto) |
Eliminar último par | popitem() | d.popitem() |
Vaciar todo | clear() | d.clear() |
🧪 Mini-ejercicios
- Crea
producto = {"sku":101,"stock":5}
. Sube el stock +3 conget()
sin provocar errores si la clave faltara. - Usa
setdefault()
para construir un índice de ciudades por país a partir de["ES;MAD","ES;SEV","PT;LIS"]
. - Fusiona
{"a":1,"b":2}
y{"b":99,"c":3}
con|
y conupdate()
. ¿Diferencias? - Elimina con
pop()
la clave"email"
de un diccionario y captura un valor por defecto si no existía. - Implementa una función
limpiar_nulos(d)
que borre pares cuya clave o valor seaNone
sin modificar el diccionario mientras lo iteras.
🏁 Cierre de la Parte 2
Ya controlas el ciclo de vida de las entradas de un dict
: leer, crear, actualizar y eliminar con seguridad.
En la Parte 3 veremos cómo iterar profesionalmente por claves, valores y pares con
keys()
, values()
, items()
, comprensiones y patrones de ordenación.
🔁 Parte 3 — Iteración y métodos (keys()
, values()
, items()
)
Hasta ahora has aprendido a crear, modificar y eliminar elementos en un dict
.
Ahora vas a recorrerlos de manera eficiente. En Python, los diccionarios permiten
iterar directamente por sus claves, valores o pares (key-value) con una sintaxis elegante y legible.
🔹 Iterar por claves
Cuando recorres un diccionario directamente en un bucle for
, obtienes sus claves por defecto.
usuario = {"nombre": "Javi", "edad": 25, "activo": True}
for clave in usuario:
print(clave)
Salida:
nombre
edad
activo
💡 Equivalente a usar for clave in usuario.keys()
.
🔹 Iterar por valores
Usa el método values()
para acceder a todos los valores.
for valor in usuario.values():
print(valor)
Salida:
Javi
25
True
values() devuelve una “vista” dinámica del diccionario (tipo dict_values
),
que se actualiza si el diccionario cambia.
v = usuario.values()
usuario["rol"] = "admin"
print(v) # dict_values(['Javi', 25, True, 'admin'])
🔹 Iterar por pares clave-valor
El método items()
devuelve pares (clave, valor)
que puedes desempaquetar fácilmente.
for clave, valor in usuario.items():
print(f"{clave}: {valor}")
Salida:
nombre: Javi
edad: 25
activo: True
💡 Este es el método más usado para procesar diccionarios, ya que ofrece acceso completo y legible.
📋 Tabla comparativa
Método | Devuelve | Ejemplo | Tipo |
---|---|---|---|
d.keys() | Claves | ['nombre','edad','activo'] | dict_keys |
d.values() | Valores | ['Javi',25,True] | dict_values |
d.items() | Pares (clave, valor) | [('nombre','Javi'),('edad',25)] | dict_items |
🔹 Recorrer con condiciones
Combina iteración y condicionales para filtrar resultados.
usuarios = {
"Ana": 28,
"Luis": 19,
"Sofía": 33,
"Pedro": 22
}
print("Usuarios mayores de 25:")
for nombre, edad in usuarios.items():
if edad > 25:
print(f" - {nombre}")
Salida:
Usuarios mayores de 25:
- Ana
- Sofía
🧮 Contar elementos o filtrar por valor
# Contar cuántos usuarios tienen edad >= 25
mayores = sum(1 for e in usuarios.values() if e >= 25)
print("Mayores de 25:", mayores)
💡 Tip: puedes usar sum()
o comprensiones generadoras para escribir bucles más Pythonic.
🔁 Iterar con índice (enumerate()
)
Si necesitas un contador en el bucle, combina enumerate()
con items()
.
for i, (clave, valor) in enumerate(usuarios.items(), start=1):
print(f"{i}. {clave} → {valor}")
Salida:
1. Ana → 28
2. Luis → 19
3. Sofía → 33
4. Pedro → 22
🧩 Iterar sobre copia de claves (seguridad al modificar)
Si necesitas eliminar elementos mientras recorres, crea una copia con list(d.keys())
para evitar errores.
edades = {"Ana":28, "Luis":19, "Pedro":17, "Sofía":33}
for nombre in list(edades.keys()):
if edades[nombre] < 18:
del edades[nombre]
print(edades) # {'Ana': 28, 'Luis': 19, 'Sofía': 33}
🔹 Comprensiones de diccionario (dict comprehensions)
Una forma compacta y elegante de construir nuevos diccionarios a partir de otros.
# Ejemplo: convertir temperaturas °C a °F
celsius = {"Madrid": 20, "Sevilla": 25, "Bilbao": 18}
fahrenheit = {ciudad: (temp * 9/5) + 32 for ciudad, temp in celsius.items()}
print(fahrenheit)
Salida:
{'Madrid': 68.0, 'Sevilla': 77.0, 'Bilbao': 64.4}
💡 También puedes aplicar filtros:
# Filtrar solo las ciudades cálidas
calidas = {c: t for c, t in celsius.items() if t >= 22}
print(calidas) # {'Sevilla': 25}
📊 Ejemplo práctico — Procesar inventario
inventario = {
"Teclado": 12,
"Ratón": 4,
"Monitor": 7,
"Impresora": 2
}
# Listar productos con stock bajo
bajos = [p for p, s in inventario.items() if s = 5}
print(suficiente)
💡 Buena práctica: usar comprensiones mejora claridad y evita bucles innecesarios.
📋 Comparativa visual de métodos
Requiere desempaquetar | Método | Ventajas |
---|---|---|
No | d.keys() | Solo claves; ideal para comprobaciones |
No | d.values() | Lectura rápida de todos los valores |
Sí | d.items() | Acceso conjunto clave/valor, más expresivo |
Opcional | enumerate() | Índice útil para depuración o impresión numerada |
🧠 Consejos pro de iteración
- Usa
items()
en el 90% de los casos: evita accesos extra y mejora legibilidad. - Usa
dict comprehensions
para filtrado o transformación en una línea. - Evita modificar el diccionario original mientras lo recorres (usa copia con
list()
). - Recuerda: las vistas
keys()
,values()
eitems()
se actualizan en vivo.
🧪 Mini-ejercicios
- Crea un diccionario
edades
y muestra sus claves confor
y conkeys()
. - Itera sobre sus valores e imprime solo los mayores de 30.
- Usa
items()
para mostrar “nombre tiene edad años”. - Construye un nuevo diccionario con solo las personas mayores de 25 (usa comprensión).
- Usa
enumerate()
sobreitems()
para imprimir una lista numerada de pares.
🏁 Cierre de la Parte 3
Acabas de dominar los tres métodos esenciales para recorrer un diccionario con precisión quirúrgica:
keys()
para las claves, values()
para los datos, y items()
para ambos a la vez.
En la siguiente parte aprenderás cómo anidar diccionarios para modelar estructuras jerárquicas y
crear configuraciones complejas o inventarios multinivel.
🏗️ Parte 4 — Diccionarios anidados y ejemplos prácticos
Un diccionario anidado es un dict
dentro de otro dict
.
Se usa para representar estructuras complejas: usuarios con varios atributos, inventarios por ciudad,
configuraciones de sistemas, etc.
🔹 Concepto básico
# Diccionario dentro de otro
usuario = {
"nombre": "Javier",
"contacto": {
"email": "javi@example.com",
"telefono": "600-111-222"
},
"roles": ["admin", "editor"]
}
print(usuario["contacto"]["email"]) # ➜ javi@example.com
💡 Accedes a los niveles internos encadenando las claves con []
.
📦 Crear diccionarios anidados dinámicamente
Puedes inicializarlos a mano o construirlos en tiempo real con setdefault()
.
inventario = {}
# Añadir productos a distintas ciudades
inventario.setdefault("Madrid", {})["Teclado"] = 15
inventario.setdefault("Madrid", {})["Ratón"] = 8
inventario.setdefault("Sevilla", {})["Monitor"] = 5
print(inventario)
# {'Madrid': {'Teclado': 15, 'Ratón': 8}, 'Sevilla': {'Monitor': 5}}
💡 setdefault() crea automáticamente el diccionario interno si no existía.
🔍 Lectura segura con get()
anidado
Si no estás seguro de que todas las claves existan, puedes combinar get()
para evitar errores.
usuarios = {
"javi": {"edad": 25, "rol": "admin"},
"ana": {"edad": 30}
}
print(usuarios.get("ana", {}).get("rol", "Sin rol")) # ➜ Sin rol
✅ Este patrón evita KeyError
y es muy usado en código de producción.
🧠 Modificar valores en diccionarios anidados
Para cambiar un valor dentro de un nivel interno, navega por las claves:
usuarios["javi"]["rol"] = "editor"
usuarios["ana"]["rol"] = "colaboradora"
print(usuarios)
💡 Si no existe el nivel intermedio, primero créalo con setdefault()
:
usuarios.setdefault("pedro", {}).setdefault("rol", "nuevo")
print(usuarios)
🧩 Ejemplo práctico 1 — Inventario multinivel
Un inventario de productos por ciudad y categoría:
inventario = {
"Madrid": {
"Periféricos": {"Teclado": 15, "Ratón": 8},
"Monitores": {"LG 24'": 5}
},
"Sevilla": {
"Periféricos": {"Ratón": 10}
}
}
# Acceso
print(inventario["Madrid"]["Periféricos"]["Teclado"]) # ➜ 15
# Modificación
inventario["Madrid"]["Monitores"]["Samsung 27'"] = 3
📘 Estructura jerárquica:
- Nivel 1 → Ciudad
- Nivel 2 → Categoría
- Nivel 3 → Producto
📊 Ejemplo práctico 2 — Configuración de aplicación
Los archivos de configuración (JSON/YAML) se traducen naturalmente a diccionarios anidados.
config = {
"servidor": {
"host": "localhost",
"puerto": 8080
},
"base_datos": {
"usuario": "root",
"password": "1234",
"nombre": "mi_app"
},
"log": {
"nivel": "INFO",
"ruta": "/var/log/app.log"
}
}
print(config["base_datos"]["usuario"]) # ➜ root
✅ Ideal para sistemas de configuración o aplicaciones web.
🧮 Ejemplo práctico 3 — Notas de alumnos
alumnos = {
"Ana": {"Matemáticas": 8.5, "Historia": 9.0},
"Luis": {"Matemáticas": 7.2, "Historia": 6.5},
"Sofía": {"Matemáticas": 9.4, "Historia": 8.7}
}
# Calcular promedios
for nombre, materias in alumnos.items():
promedio = sum(materias.values()) / len(materias)
print(f"{nombre}: promedio = {promedio:.2f}")
💡 Ejemplo real de cómo recorrer y procesar estructuras anidadas con items()
.
⚙️ Ejemplo práctico 4 — API simulada (diccionario de diccionarios)
api = {
"usuarios": {
"001": {"nombre": "Ana", "rol": "admin"},
"002": {"nombre": "Luis", "rol": "editor"}
},
"posts": {
"101": {"titulo": "Introducción a Python", "autor_id": "001"},
"102": {"titulo": "Estructuras de datos", "autor_id": "002"}
}
}
# Buscar el autor del post 102
autor_id = api["posts"]["102"]["autor_id"]
autor = api["usuarios"][autor_id]["nombre"]
print("Autor del post 102:", autor)
💡 Este patrón es idéntico al de bases de datos relacionales o respuestas JSON anidadas.
🧱 Ejemplo práctico 5 — Fusión de diccionarios anidados
Para fusionar estructuras con varios niveles, usa update()
o el operador |
a nivel superior,
pero escribe una función si quieres combinar niveles internos.
def merge_nested(d1, d2):
for k, v in d2.items():
if k in d1 and isinstance(d1[k], dict) and isinstance(v, dict):
merge_nested(d1[k], v)
else:
d1[k] = v
return d1
a = {"Madrid": {"Ratón": 5}}
b = {"Madrid": {"Teclado": 10}, "Sevilla": {"Monitor": 3}}
merge_nested(a, b)
print(a)
# {'Madrid': {'Ratón': 5, 'Teclado': 10}, 'Sevilla': {'Monitor': 3}}
✅ Este patrón se usa en frameworks de configuración y librerías como pydantic o OmegaConf.
📋 Tabla resumen de operaciones anidadas
Operación | Ejemplo | Resultado |
---|---|---|
Acceso directo | d["nivel1"]["nivel2"] | Valor interno |
Acceso seguro | d.get("nivel1", {}).get("nivel2") | Evita KeyError |
Creación si falta | d.setdefault("nivel1", {}) | Crea nivel vacío |
Actualización interna | d["nivel1"]["nivel2"] = valor | Modifica un campo interno |
Fusión profunda | merge_nested(a,b) | Une diccionarios recursivamente |
🧩 Ejemplo práctico 6 — Generar diccionarios anidados por comprensión
Puedes construir estructuras de varios niveles usando dict comprehensions anidadas.
# Tabla de multiplicar 1-3
tabla = {i: {j: i*j for j in range(1, 4)} for i in range(1, 4)}
print(tabla)
# {1:{1:1,2:2,3:3}, 2:{1:2,2:4,3:6}, 3:{1:3,2:6,3:9}}
💡 Muy usado para generar configuraciones o estructuras jerárquicas automáticamente.
🧪 Mini-ejercicios
- Crea un diccionario
empresa
que contenga departamentos y empleados con su salario. - Agrega un nuevo departamento si no existe usando
setdefault()
. - Accede de forma segura al salario de un empleado que podría no existir.
- Crea una función
buscar_producto(inventario, ciudad, nombre)
que devuelva el stock o “Sin datos”. - Fusiona dos inventarios anidados con la función
merge_nested()
.
🏁 Cierre de la Parte 4
Los diccionarios anidados te permiten modelar datos complejos con jerarquías naturales.
Ahora puedes representar sistemas completos (usuarios, inventarios, configuraciones, catálogos) en estructuras Python simples y potentes.
En la Parte 5 verás buenas prácticas, rendimiento y ejercicios integradores para cerrar el tema.
💡 Parte 5 — Buenas prácticas, rendimiento y ejercicios
Ya dominas cómo crear, modificar, recorrer y anidar diccionarios.
Ahora vamos a cerrar el tema con buenas prácticas, optimización y algunos
ejercicios integradores que consolidarán todo lo aprendido.
🧭 1. Buenas prácticas de estilo y legibilidad
- ✅ Usa nombres descriptivos para las claves:
{"nombre":"Javi","edad":25}
es mejor que{"n":"J","e":25}
. - ✅ Si el contenido representa una entidad (usuario, producto…), nómbralo en singular.
- ✅ Usa acceso seguro (
get()
,setdefault()
) cuando la clave puede faltar. - ✅ Evita anidar más de 3 niveles; usa clases o dataclasses si la jerarquía es profunda.
- ✅ Formatea tu código: PEP 8 recomienda 4 espacios por nivel y nombres en minúsculas con guiones bajos (
snake_case
).
# Mal ejemplo
info = {"a": {"b": {"c": 1}}}
# Mejor
config_bd = {
"host": "localhost",
"puerto": 5432,
"usuario": "admin"
}
🧩 2. Buenas prácticas al modificar y fusionar
- ✅ Evita sobrescribir datos sin verificar la existencia previa de la clave.
- ✅ Para fusionar diccionarios, usa:
a | b
(crea nuevo) oa |= b
(modifica el existente). - ✅ Si los diccionarios tienen niveles anidados, usa una función recursiva como
merge_nested()
. - ✅ Si vas a eliminar claves durante una iteración, recorre primero una copia con
list()
.
# Ejemplo seguro de fusión
default_cfg = {"modo":"produccion","debug":False}
user_cfg = {"debug":True}
cfg_final = default_cfg | user_cfg
print(cfg_final)
⚙️ 3. Rendimiento y eficiencia
Los diccionarios en Python están optimizados con tablas hash, lo que permite acceso en O(1).
Sin embargo, hay escenarios donde puedes optimizar aún más.
Operación | Complejidad | Consejo |
---|---|---|
Acceso / inserción | O(1) | Usa diccionarios para búsquedas rápidas |
Eliminación | O(1) | Evita hacerlo dentro de un bucle de iteración directa |
Fusión (|, update) | O(n) | Fusiona grandes estructuras una sola vez |
Iteración con items() | O(n) | Usa items() en lugar de claves/valores separados |
💡 Pro tip: Si tienes muchos accesos repetidos, guarda la referencia local:
# En lugar de esto
for key in data:
procesar(data[key])
# Haz esto
d = data
for key in d:
procesar(d[key])
Es más eficiente porque evita búsquedas repetidas en memoria.
🧮 4. Uso combinado con otras estructuras
Los diccionarios suelen combinarse con listas y tuplas para representar colecciones de objetos.
usuarios = [
{"id": 1, "nombre": "Ana", "rol": "admin"},
{"id": 2, "nombre": "Luis", "rol": "editor"},
{"id": 3, "nombre": "Sofía", "rol": "viewer"}
]
# Obtener nombres de todos los usuarios admin
admins = [u["nombre"] for u in usuarios if u["rol"] == "admin"]
print(admins) # ['Ana']
💡 Este patrón es muy común en desarrollo web, APIs y procesamiento de datos JSON.
🧰 5. Diccionarios especializados del módulo collections
Python incluye versiones optimizadas de dict
para casos concretos:
Tipo | Importación | Uso principal |
---|---|---|
defaultdict | from collections import defaultdict | Crea valores por defecto automáticamente |
OrderedDict | from collections import OrderedDict | Preserva orden de inserción (útil antes de Python 3.7) |
Counter | from collections import Counter | Cuenta ocurrencias fácilmente |
from collections import defaultdict, Counter
# defaultdict
equipos = defaultdict(list)
equipos["A"].append("Javi")
equipos["A"].append("Luis")
print(equipos) # defaultdict(list, {'A': ['Javi','Luis']})
# Counter
letras = Counter("banana")
print(letras) # Counter({'a':3, 'n':2, 'b':1})
🧠 6. Antipatrones comunes (y cómo evitarlos)
- ❌ Acceder a una clave inexistente sin comprobación →
KeyError
- ❌ Usar objetos mutables como claves →
TypeError
- ❌ Iterar y modificar al mismo tiempo → resultados impredecibles
- ❌ Repetir claves en el literal → se sobrescriben sin aviso
- ❌ Convertir dicts en listas sin usar
.items()
correctamente
✅ Solución: usar .get()
, setdefault()
y comprensiones.
📋 7. Tabla de referencia rápida
Acción | Método o patrón | Ejemplo |
---|---|---|
Leer valor | d.get(k, defecto) | usuario.get("email","N/D") |
Agregar clave | d[k] = v | usuario["rol"]="admin" |
Actualizar | d.update({...}) | perfil.update(email="a@b.com") |
Eliminar | d.pop(k, defecto) | d.pop("edad",None) |
Iterar pares | for k,v in d.items() | print(k,v) |
Filtrar | Comprensión | {k:v for k,v in d.items() if v>0} |
Fusión | d1 | d2 | a | b |
🧩 8. Ejercicios integradores
🔸 Ejercicio 1 — Sistema de notas
notas = {
"Ana": {"Matemáticas": 9, "Lengua": 7},
"Luis": {"Matemáticas": 6, "Lengua": 8}
}
# 1. Añade una nueva asignatura "Inglés" a todos con valor 0.
# 2. Calcula el promedio total por alumno.
# 3. Crea un nuevo dict con los promedios.
🔸 Ejercicio 2 — Catálogo de productos
productos = {
"Teclado": {"precio": 25, "stock": 10},
"Ratón": {"precio": 15, "stock": 0},
"Monitor": {"precio": 120, "stock": 5}
}
# Muestra solo los productos disponibles (stock > 0)
# Calcula el valor total del inventario
🔸 Ejercicio 3 — Conteo de palabras
texto = "python es poderoso y python es simple"
palabras = texto.split()
contador = {}
for p in palabras:
contador[p] = contador.get(p, 0) + 1
print(contador)
💡 Luego reemplázalo por Counter(palabras)
y compara resultados.
🎯 9. Proyecto final — “Gestor de alumnos”
Construye un pequeño sistema en Python que:
- Permita registrar alumnos y asignaturas.
- Guarde los datos en un diccionario anidado.
- Permita consultar y modificar calificaciones.
- Calcule automáticamente el promedio global.
alumnos = {}
while True:
nombre = input("Nombre del alumno (o salir): ")
if nombre.lower() == "salir":
break
alumnos.setdefault(nombre, {})
materia = input("Asignatura: ")
nota = float(input("Nota: "))
alumnos[nombre][materia] = nota
print("\nListado final:")
for alumno, materias in alumnos.items():
promedio = sum(materias.values()) / len(materias)
print(f"{alumno}: promedio {promedio:.2f}")
💡 Este ejercicio integra lectura, escritura, control de flujo y estructuras anidadas.
🏁 Conclusión
Los diccionarios son una de las estructuras más potentes de Python.
Representan datos reales con flexibilidad, eficiencia y claridad.
Dominar su sintaxis y patrones te permitirá trabajar con JSON, APIs, bases de datos y estructuras complejas sin esfuerzo.
Lo que has aprendido:
- Crear y acceder a pares clave-valor.
- Modificar y eliminar elementos de forma segura.
- Recorrer y filtrar con
keys()
,values()
eitems()
. - Gestionar estructuras anidadas con
setdefault()
yget()
. - Aplicar buenas prácticas, optimización y herramientas de
collections
.
¡Enhorabuena! Has completado el estudio de los diccionarios en Python.
Estás listo para seguir con el siguiente tema: Conjuntos (set) y operaciones de colección.