Diccionarios en Python: acceso O(1) y estructuras de datos con nombre
- Qué es un diccionario y por qué es tan potente
- Crear diccionarios: todas las sintaxis
- Leer y modificar valores: [] vs get()
- Iterar: keys(), values(), items()
- Métodos esenciales del diccionario
- Dict comprehension: construir dicts en una línea
- Patrones avanzados: defaultdict, Counter, merge
- Diccionarios anidados y estructuras JSON
- Programa completo: análisis de texto
- Errores clásicos
- Preguntas frecuentes
persona["nombre"] es sokrático en su esencia: preguntar directamente, recibir la respuesta en O(1) sin recorrer toda la estructura. Y dict.get("respuesta_definitiva", None) es, quizás, la línea más socrática de todas.
El diccionario (dict) almacena pares clave: valor y permite acceder a cualquier valor en tiempo O(1), independientemente del tamaño. Es la estructura más potente y versátil de Python para organizar datos con nombre: configuraciones, registros, contadores, agrupaciones. Cualquier programa Python real usa diccionarios constantemente.
🗂️ Crear diccionarios: todas las sintaxis
# Sintaxis con llaves (la más común)
vacio = {}
persona = {"nombre": "Ana", "edad": 28, "ciudad": "Madrid"}
config = {"debug": True, "puerto": 8080, "host": "localhost"}
# Constructor dict() con argumentos clave=valor
config2 = dict(debug=True, puerto=8080, host="localhost")
# Dict desde lista de tuplas (pares clave-valor)
pairs = [("a", 1), ("b", 2), ("c", 3)]
desde_pares = dict(pairs) # {"a":1, "b":2, "c":3}
# Dict desde dos listas con zip
claves = ["nombre", "edad", "ciudad"]
valores = ["Luis", 22, "Barcelona"]
combinado = dict(zip(claves, valores)) # {"nombre":"Luis","edad":22,...}
# Dict comprehension
cuadrados = {x: x**2 for x in range(1, 6)} # {1:1, 2:4, 3:9, 4:16, 5:25}
# Longitud y comprobaciones básicas
print(len(persona)) # 3
print("nombre" in persona) # True ← busca en claves
print("Ana" in persona) # False ← "Ana" es un valor, no una clave
print("Ana" in persona.values()) # True ← buscar en valores
🔍 Leer y modificar valores: [] vs get()
persona = {"nombre": "Ana", "edad": 28, "ciudad": "Madrid"}
# Leer con corchetes — lanza KeyError si no existe
print(persona["nombre"]) # "Ana"
print(persona["email"]) # KeyError: 'email' ← rompe el programa
# Leer con get() — devuelve None si no existe (sin error)
print(persona.get("nombre")) # "Ana"
print(persona.get("email")) # None
print(persona.get("email", "sin email")) # "sin email" ← valor por defecto
# Modificar / añadir
persona["edad"] = 29 # modifica valor existente
persona["email"] = "ana@eg.com" # añade nueva clave
persona.update({"edad": 30, "pais": "España"}) # actualiza múltiples claves
persona.update(pais="España", rol="admin") # con kwargs también
# setdefault: añade solo si la clave NO existe
persona.setdefault("puntos", 0) # si "puntos" no existe, lo crea con 0
persona.setdefault("nombre", "X") # "nombre" ya existe → no cambia nada
print(persona["puntos"]) # 0
print(persona["nombre"]) # "Ana" ← intacto
# Eliminar
del persona["ciudad"] # KeyError si no existe
persona.pop("email") # elimina y devuelve el valor
persona.pop("telefono", None) # sin error si no existe ✅
persona.popitem() # elimina y devuelve el ÚLTIMO par insertado
persona.clear() # vacía el diccionario
🔄 Iterar: keys(), values(), items()
persona = {"nombre": "Ana", "edad": 28, "ciudad": "Madrid"}
# Iterar solo por claves (comportamiento por defecto)
for clave in persona:
print(clave) # nombre, edad, ciudad
for clave in persona.keys(): # equivalente, más explícito
print(clave)
# Iterar solo por valores
for valor in persona.values():
print(valor) # Ana, 28, Madrid
# Iterar por pares clave-valor (lo más habitual)
for clave, valor in persona.items():
print(f"{clave}: {valor}")
# nombre: Ana
# edad: 28
# ciudad: Madrid
# Verificar si una clave existe
if "nombre" in persona:
print(persona["nombre"])
# Ordenar al iterar
inventario = {"manzana": 50, "pera": 30, "kiwi": 80, "uva": 15}
# Ordenar por clave
for nombre in sorted(inventario):
print(f"{nombre}: {inventario[nombre]}")
# Ordenar por valor (descendente)
for nombre, cantidad in sorted(inventario.items(), key=lambda x: -x[1]):
print(f"{nombre}: {cantidad}")
# kiwi: 80, manzana: 50, pera: 30, uva: 15
# Obtener la clave con el mayor valor
mas_stock = max(inventario, key=inventario.get) # "kiwi"
🔧 Métodos esenciales del diccionario
d = {"a": 1, "b": 2, "c": 3}
# Vistas (objetos dinámicos, se actualizan con el dict)
d.keys() # dict_keys(['a', 'b', 'c'])
d.values() # dict_values([1, 2, 3])
d.items() # dict_items([('a',1), ('b',2), ('c',3)])
# Copiar (copia superficial)
copia = d.copy()
copia = dict(d) # equivalente
# Merge (Python 3.9+)
d1 = {"a": 1, "b": 2}
d2 = {"b": 3, "c": 4}
merged = d1 | d2 # {"a":1, "b":3, "c":4} ← d2 tiene prioridad
d1 |= d2 # actualiza d1 in-place
# Merge en versiones anteriores a 3.9
merged = {**d1, **d2} # desempaquetado: d2 sobreescribe claves comunes
d1.update(d2) # actualiza d1 in-place
# fromkeys: crear dict con claves dadas y mismo valor inicial
campos = ["nombre", "email", "telefono"]
registro_vacio = dict.fromkeys(campos, "")
# {"nombre":"", "email":"", "telefono":""}
registro_ninguno = dict.fromkeys(campos, None)
# Eliminar duplicados de lista manteniendo orden
lista = [3, 1, 4, 1, 5, 9, 2, 6, 5]
sin_dups = list(dict.fromkeys(lista)) # [3, 1, 4, 5, 9, 2, 6]
⚡ Dict comprehension: construir dicts en una línea
# Sintaxis: {clave: valor for item in iterable if condición}
# Cuadrados de 1 a 5
cuadrados = {x: x**2 for x in range(1, 6)}
# {1:1, 2:4, 3:9, 4:16, 5:25}
# Invertir un diccionario (clave ↔ valor)
original = {"a": 1, "b": 2, "c": 3}
invertido = {v: k for k, v in original.items()}
# {1:"a", 2:"b", 3:"c"}
# ⚠️ Solo funciona si los valores son únicos y hashables
# Filtrar un diccionario (solo pares que cumplen condición)
inventario = {"manzana": 50, "pera": 5, "kiwi": 80, "uva": 2}
stock_alto = {k: v for k, v in inventario.items() if v > 10}
# {"manzana": 50, "kiwi": 80}
# Transformar valores
precios_eur = {"café": 2.5, "zumo": 3.0, "agua": 1.0}
cambio = 1.08
precios_usd = {producto: round(precio * cambio, 2)
for producto, precio in precios_eur.items()}
# {"café": 2.7, "zumo": 3.24, "agua": 1.08}
# Crear dict desde dos listas
nombres = ["Ana", "Luis", "Marta"]
edades = [28, 22, 35]
personas = {nombre: edad for nombre, edad in zip(nombres, edades)}
# {"Ana":28, "Luis":22, "Marta":35}
# Con condición en la clave
palabras = ["python", "java", "c++", "ruby", "go"]
longitudes = {p: len(p) for p in palabras if len(p) > 3}
# {"python":6, "java":4, "ruby":4}
🔬 Patrones avanzados: defaultdict, Counter, merge
from collections import defaultdict, Counter
# --- defaultdict: valor por defecto automático ---
# Sin defaultdict (código verbose):
grupos = {}
for nombre, ciudad in [("Ana","Madrid"),("Luis","Madrid"),("Marta","Barcelona")]:
if ciudad not in grupos:
grupos[ciudad] = []
grupos[ciudad].append(nombre)
# Con defaultdict(list) (Pythonic):
grupos = defaultdict(list)
for nombre, ciudad in [("Ana","Madrid"),("Luis","Madrid"),("Marta","Barcelona")]:
grupos[ciudad].append(nombre) # la clave se crea sola con []
# defaultdict(list, {"Madrid":["Ana","Luis"], "Barcelona":["Marta"]})
# defaultdict(int) para contadores
visitas = defaultdict(int)
log = ["inicio","inicio","login","inicio","logout","login"]
for evento in log:
visitas[evento] += 1
# defaultdict(int, {"inicio":3, "login":2, "logout":1})
# defaultdict(set) para agrupar en conjuntos (sin duplicados)
etiquetas = defaultdict(set)
datos = [("artículo1","python"),("artículo1","django"),("artículo2","python")]
for articulo, tag in datos:
etiquetas[articulo].add(tag)
# {"artículo1":{"python","django"}, "artículo2":{"python"}}
# --- Counter: contador de ocurrencias ---
texto = "esta es una frase de ejemplo con palabras repetidas con palabras"
palabras = texto.split()
conteo = Counter(palabras)
print(conteo)
# Counter({"palabras":2, "con":2, "esta":1, ...})
print(conteo.most_common(3)) # las 3 más frecuentes
# [("palabras",2), ("con",2), ("esta",1)]
# Operaciones entre Counters
c1 = Counter(a=4, b=2, c=1)
c2 = Counter(a=1, b=2, d=3)
print(c1 + c2) # suma: Counter({'a':5,'b':4,'d':3,'c':1})
print(c1 - c2) # resta (solo positivos): Counter({'a':3,'c':1})
print(c1 & c2) # mínimo: Counter({'b':2,'a':1})
print(c1 | c2) # máximo: Counter({'a':4,'d':3,'b':2,'c':1})
🏗️ Diccionarios anidados y estructuras JSON
# Diccionario anidado típico (similar a JSON)
usuario = {
"id": 42,
"nombre": "Ana García",
"rol": "admin",
"config": {
"tema": "oscuro",
"idioma": "es",
"notificaciones": {
"email": True,
"push": False,
}
},
"etiquetas": ["python", "django", "api"]
}
# Acceso anidado
print(usuario["config"]["idioma"]) # "es"
print(usuario["config"]["notificaciones"]["email"]) # True
print(usuario["etiquetas"][0]) # "python"
# Acceso seguro a claves anidadas con get() encadenado
tema = usuario.get("config", {}).get("tema", "claro")
print(tema) # "oscuro"
# Modificar anidado
usuario["config"]["tema"] = "claro"
usuario["config"]["notificaciones"]["push"] = True
# Trabajar con JSON (formato intercambiable con dict Python)
import json
# Dict → JSON string
json_str = json.dumps(usuario, ensure_ascii=False, indent=2)
print(json_str[:100])
# JSON string → dict
datos = json.loads(json_str)
print(datos["nombre"]) # "Ana García"
# Leer / escribir JSON en fichero
with open("usuario.json", "w", encoding="utf-8") as f:
json.dump(usuario, f, ensure_ascii=False, indent=2)
with open("usuario.json", "r", encoding="utf-8") as f:
cargado = json.load(f)
🛠️ Programa completo: análisis de texto
"""
Analizador de texto: demuestra patrones reales con diccionarios.
Cuenta palabras, caracteres, frecuencias y genera estadísticas.
"""
import re
from collections import Counter, defaultdict
def limpiar(texto):
"""Normaliza el texto: minúsculas y solo letras/espacios."""
return re.sub(r'[^a-záéíóúüñ\s]', '', texto.lower())
def analizar(texto):
"""Analiza el texto y devuelve un dict con estadísticas."""
palabras = limpiar(texto).split()
if not palabras:
return {}
conteo = Counter(palabras)
# Distribuir por longitud de palabra
por_longitud = defaultdict(list)
for p in set(palabras):
por_longitud[len(p)].append(p)
# Palabras únicas por inicial
por_inicial = defaultdict(set)
for p in palabras:
por_inicial[p[0]].add(p)
return {
"total_palabras": len(palabras),
"palabras_unicas": len(conteo),
"total_caracteres": sum(len(p) for p in palabras),
"longitud_media": round(sum(len(p) for p in palabras) / len(palabras), 2),
"mas_frecuentes": conteo.most_common(5),
"hapax": [p for p, n in conteo.items() if n == 1], # aparecen 1 vez
"por_longitud": {k: sorted(v) for k, v in sorted(por_longitud.items())},
"por_inicial": {k: sorted(v) for k, v in sorted(por_inicial.items())},
"densidad_lexica": round(len(conteo) / len(palabras) * 100, 1), # %
}
def mostrar_informe(stats):
"""Imprime el informe de estadísticas."""
if not stats:
print(" Sin datos para analizar.")
return
print(f"\n 📊 ESTADÍSTICAS DEL TEXTO")
print(f" {'─'*40}")
print(f" Total palabras: {stats['total_palabras']}")
print(f" Palabras únicas: {stats['palabras_unicas']}")
print(f" Total caracteres: {stats['total_caracteres']}")
print(f" Longitud media: {stats['longitud_media']} letras")
print(f" Densidad léxica: {stats['densidad_lexica']}%")
print(f"\n 🔝 5 PALABRAS MÁS FRECUENTES:")
for palabra, n in stats["mas_frecuentes"]:
barra = "█" * n
print(f" {palabra:<15} {n:>3}x {barra}")
print(f"\n 🔤 DISTRIBUCIÓN POR LONGITUD:")
for longitud, lista in stats["por_longitud"].items():
print(f" {longitud} letras: {len(lista)} palabras únicas → {', '.join(lista[:4])}{'…' if len(lista)>4 else ''}")
print(f"\n 📝 HAPAX LEGOMENA (aparecen solo 1 vez): {len(stats['hapax'])} palabras")
print(f" {', '.join(sorted(stats['hapax'])[:10])}{'…' if len(stats['hapax'])>10 else ''}")
# Texto de ejemplo
TEXTO_EJEMPLO = """
Python es un lenguaje de programación de alto nivel conocido por su sintaxis clara
y legible. Python se utiliza en ciencia de datos, desarrollo web, automatización y
inteligencia artificial. La comunidad de Python es una de las más activas del mundo
del software. Python tiene una filosofía de diseño que enfatiza la legibilidad del
código y permite a los programadores expresar conceptos en menos líneas de código.
"""
print("Analizando texto de ejemplo...")
stats = analizar(TEXTO_EJEMPLO)
mostrar_informe(stats)
# Modo interactivo
print("\n" + "═"*50)
while True:
print("\n1. Analizar texto nuevo 2. Comparar dos textos 3. Salir")
opcion = input("Opción: ").strip()
if opcion == "1":
print(" Escribe el texto (Enter dos veces para terminar):")
lineas = []
while True:
linea = input()
if not linea:
break
lineas.append(linea)
stats = analizar(" ".join(lineas))
mostrar_informe(stats)
elif opcion == "2":
print(" TEXTO A:")
lineas_a = []
while True:
l = input()
if not l: break
lineas_a.append(l)
print(" TEXTO B:")
lineas_b = []
while True:
l = input()
if not l: break
lineas_b.append(l)
sa = analizar(" ".join(lineas_a))
sb = analizar(" ".join(lineas_b))
pa = set(limpiar(" ".join(lineas_a)).split())
pb = set(limpiar(" ".join(lineas_b)).split())
print(f"\n Vocabulario común: {len(pa & pb)} palabras")
print(f" Solo en texto A: {len(pa - pb)} palabras")
print(f" Solo en texto B: {len(pb - pa)} palabras")
print(f" Densidad léxica A: {sa.get('densidad_lexica', 'N/A')}%")
print(f" Densidad léxica B: {sb.get('densidad_lexica', 'N/A')}%")
elif opcion == "3":
print("Hasta luego.")
break
🐛 Errores clásicos con diccionarios
1. KeyError al acceder con corchetes
d = {"a": 1}
d["b"] # KeyError: 'b'
d.get("b") # None ✅
d.get("b", 0) # 0 ✅
2. Modificar un diccionario mientras lo iteras
# ❌ RuntimeError: dictionary changed size during iteration
for clave in d:
if condicion(clave):
del d[clave]
# ✅ Iterar sobre una copia de las claves
for clave in list(d.keys()):
if condicion(clave):
del d[clave]
3. Usar valores mutables como claves
d = {}
d[[1, 2]] = "valor" # TypeError: unhashable type: 'list'
d[(1, 2)] = "valor" # ✅ tupla es hashable
4. Guardar el resultado de dict.update()
resultado = d.update({"k": "v"}) # update devuelve None
# resultado = None ← trampa
# ✅ Para obtener un nuevo dict fusionado:
nuevo = d | {"k": "v"} # Python 3.9+
nuevo = {**d, "k": "v"} # Python < 3.9
5. Iterar por valores cuando necesitas claves
for valor in d: # ← itera CLAVES, no valores
print(valor) # imprime "nombre", "edad"...
for valor in d.values(): # ← itera valores ✅
print(valor) # imprime "Ana", 28...
✅ Resumen y próximos pasos
Los diccionarios son la estructura de datos más poderosa de Python para organizar información por nombre. Acceso O(1), flexibilidad total de tipos, y un ecosistema de herramientas (defaultdict, Counter, json) que los hace omnipresentes. Dominar el diccionario es dominar el 50% de los patrones Python del mundo real.
La siguiente lección: conjuntos (set), donde la unicidad y las operaciones matemáticas de teoría de conjuntos resuelven problemas que serían engorrosos con listas o dicts.
❓ Preguntas frecuentes
❓ Preguntas frecuentes sobre Diccionarios en Python: acceso O(1) y estructuras de datos con nombre
Las dudas más comunes respondidas de forma clara y directa.
💬 Foro de discusión
¿Tienes dudas sobre Diccionarios en Python: acceso O(1) y estructuras de datos con nombre? Comparte tu pregunta con la comunidad.
Todavía no hay mensajes. ¡Sé el primero en participar!