Diccionarios en Python: acceso O(1) y estructuras de datos con nombre

📅 Actualizado en marzo 2026 📊 Nivel: Principiante ⏱️ 22 min de lectura
?
ἓν οἶδα ὅτι οὐδὲν οἶδα
«Solo sé que no sé nada»
Sócrates · Filósofo ateniense · 470 a.C. – 399 a.C.
Sócrates no escribió libros. Su método era la pregunta: preguntar el nombre de algo, buscar su definición precisa, distinguirlo de todo lo demás. El diccionario de Python es la implementación computacional de ese impulso: estructurar el conocimiento como pares nombre–valor, donde cada clave es una pregunta y cada valor su respuesta. 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
Archivador metálico antiguo con fichas organizadas por letras del alfabeto en un fichero de biblioteca
Un fichero de biblioteca: cada ficha tiene un identificador (clave) y la información asociada (valor). Acceso directo, sin recorrer todo el cajón. Exactamente como un diccionario Python con búsqueda O(1). Fuente: Pexels (licencia libre).

🔄 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]
Infografía de operaciones sobre diccionarios Python: crear, leer, modificar, eliminar, iterar y fusionar con sintaxis completa
Mapa completo de operaciones sobre diccionarios: creación, acceso con [] y get(), modificación, eliminación, iteración y fusión. Incluye los métodos más utilizados con su descripción y comportamiento. Infografía: Ciberaula.

⚡ 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)
Frascos de especias ordenados en estante con etiquetas escritas a mano que identifican cada contenido
Cada frasco etiquetado: una clave que da acceso inmediato a su contenido sin necesidad de abrir todos los demás. La tabla hash del diccionario Python funciona exactamente así. Fuente: Pexels (licencia libre).

🛠️ 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
Ficha de referencia completa del diccionario Python: métodos, dict comprehension, defaultdict y Counter con sintaxis
Ficha de referencia del diccionario Python. Métodos del objeto dict, dict comprehension, defaultdict y Counter de collections. Todo lo que necesitas para trabajar con diccionarios de forma fluida. Infografía: Ciberaula.

🐛 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.

dict["clave"] lanza KeyError si la clave no existe, lo que detiene el programa. dict.get("clave") devuelve None por defecto, y puedes especificar otro valor: dict.get("clave", "por_defecto"). Usa corchetes cuando estés seguro de que la clave existe y quieras que el error sea visible si no. Usa get() cuando la ausencia de la clave sea un caso válido que tu código debe manejar. En general, get() produce código más robusto.
Sí, desde Python 3.7 (y como detalle de implementación en 3.6). Los pares clave-valor se iteran en el orden en que fueron insertados. Antes de Python 3.7 no había garantía de orden, y si necesitabas orden explícito debías usar collections.OrderedDict. Hoy, OrderedDict sigue siendo útil principalmente cuando necesitas mover elementos al inicio o al final (move_to_end) o cuando la igualdad entre dos dicts debe considerar el orden.
Python 3.9+ introduce el operador |: merged = d1 | d2 crea un nuevo dict con las claves de ambos (d2 tiene prioridad en caso de colisión). También puedes usar d1 |= d2 para actualizar d1 in-place. En versiones anteriores, la forma idiomática era merged = {**d1, **d2}, que desempaqueta ambos dicts en uno nuevo. dict.update() también fusiona, pero modifica el dict receptor in-place y devuelve None.
Usa defaultdict cuando necesites que las claves inexistentes tengan un valor por defecto automático, especialmente al acumular valores: agrupar elementos (defaultdict(list)), contar (defaultdict(int)), o acumular sumas. Evita el patrón verbose "if key not in d: d[key] = []". Si solo necesitas un valor por defecto puntualmente, dict.get(key, default) o dict.setdefault(key, default) son suficientes sin importar defaultdict.
Los diccionarios en Python no se "ordenan" in-place, pero puedes crear una vista ordenada de sus pares: sorted(d.items(), key=lambda x: x[1]) devuelve una lista de tuplas (clave, valor) ordenadas por valor. Para reconstruir un dict ordenado: dict(sorted(d.items(), key=lambda x: x[1])). Para orden descendente añade reverse=True. Otra opción es usar Counter.most_common() si el diccionario es un contador.
Cualquier objeto hashable puede ser clave: integers, floats, strings, bytes, tuples (que contengan solo tipos hashables), y objetos de clases con __hash__ definido. No pueden ser claves: listas, diccionarios, conjuntos (son mutables y no hashables). La hashabilidad garantiza que la clave pueda usarse en la tabla hash interna del dict para el acceso O(1). Si necesitas una "clave" que sea una lista, convierte primero a tupla: dict[tuple(mi_lista)].
Valora este artículo

💬 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.

¿Tienes cuenta? o comenta como invitado ↓

Todavía no hay mensajes. ¡Sé el primero en participar!