Fundamentos

Diccionarios en Python (dict): creación, métodos, recorrido, ordenación y frozendict

Actualizado: · Skillaria.Top

Un diccionario en Python (dict) es una colección modificable de pares «clave — valor» que devuelve el valor por su clave casi al instante; se crea con llaves, por ejemplo {"café": 250}, o con la función dict(). Las claves son únicas y deben ser hashables; desde Python 3.7 se conserva el orden de inserción, y en Python 3.15 apareció la variante inmutable integrada, frozendict.

Un diccionario en Python (dict) es una colección de pares «clave — valor» en la que el valor se encuentra por su clave casi al instante, sin importar cuántos registros haya. Si necesita sumar los gastos por categoría, guardar la configuración o buscar un usuario por su id, use un diccionario. A continuación verá todo lo necesario para trabajar con dict: creación, métodos, recorrido, ordenación, unión y la novedad de Python 3.15, frozendict.

Qué es un diccionario en Python

Un diccionario guarda pares en los que cada clave es única: si escribe un valor con una clave que ya existe, el anterior se reemplaza. Por dentro es una tabla hash, así que buscar, añadir y eliminar por clave funcionan en tiempo constante de media, sin recorrer todo el contenido.

La clave solo puede ser un objeto hashable: una cadena, un número o una tupla de valores inmutables. Una lista no puede ser clave y provoca TypeError: unhashable type: 'list'. Los valores, en cambio, pueden ser cualquier cosa, también otros diccionarios o listas.

Desde Python 3.7, el diccionario conserva con garantía el orden de inserción: al recorrerlo, las claves salen en el orden en que se añadieron.

Cómo crear un diccionario

Hay varias formas de crear un diccionario, y todas aparecen en código real.

gastos = {"Comida": 1200, "Taxi": 450}      # literal
vacio = {}                                   # diccionario vacío
tambien_vacio = dict()                       # lo mismo con la función dict
usuario = dict(nombre="Ana", edad=30)        # argumentos con nombre
pares = dict([("a", 1), ("b", 2)])           # a partir de una lista de pares
claves = dict.fromkeys(["x", "y"], 0)        # {'x': 0, 'y': 0}

Fíjese en que las llaves vacías {} crean un diccionario vacío, no un conjunto. Un conjunto vacío se crea solo con set().

Una forma cómoda de construir un diccionario a partir de dos listas es zip:

nombres = ["Comida", "Taxi", "Cine"]
importes = [1200, 450, 600]
gastos = dict(zip(nombres, importes))
print(gastos)   # {'Comida': 1200, 'Taxi': 450, 'Cine': 600}

Acceso por clave: d[k] y get

El valor se lee con corchetes. Si la clave no existe, Python lanza KeyError. Para evitar el error, use el método get: devuelve None o el valor por defecto que indique.

gastos = {"Comida": 1200}
print(gastos["Comida"])            # 1200
print(gastos.get("Cine"))          # None
print(gastos.get("Cine", 0))       # 0
gastos["Cine"]                     # KeyError: 'Cine'

Para comprobar si una clave existe en el diccionario se usa el operador in: "Comida" in gastos devuelve True. Se comprueban las claves, no los valores; para buscar valores se escribe 1200 in gastos.values().

Añadir, modificar y eliminar

Añadir o actualizar un registro es una sola línea de asignación. Para contar, resulta muy cómoda la combinación con get:

gastos = {}
for categoria, importe in [("Comida", 250), ("Taxi", 300), ("Comida", 400)]:
    gastos[categoria] = gastos.get(categoria, 0) + importe
print(gastos)   # {'Comida': 650, 'Taxi': 300}

Una clave se elimina con del d[k] (provoca KeyError si no existe) o con d.pop(k, default), que además devuelve el valor. Para vaciar el diccionario por completo se usa d.clear(). El número de pares es len(d).

Métodos de los diccionarios: chuleta

MétodoQué hace
get(k, default)el valor de la clave, o default sin error
setdefault(k, default)devuelve el valor; si no hay clave, primero escribe default
update(otro)añade y sobrescribe los pares de otro diccionario
pop(k, default)elimina la clave y devuelve el valor
popitem()elimina y devuelve el último par añadido
keys(), values(), items()vistas de las claves, los valores y los pares
copy()copia superficial
clear()vacía el diccionario
fromkeys(claves, valor)nuevo diccionario con el mismo valor en todas las claves

setdefault: llenar un diccionario de listas

El método setdefault resulta útil cuando los valores son listas y hay que agrupar:

grupos = {}
for producto, cat in [("café", "Comida"), ("metro", "Transporte"), ("pizza", "Comida")]:
    grupos.setdefault(cat, []).append(producto)
print(grupos)   # {'Comida': ['café', 'pizza'], 'Transporte': ['metro']}

update: actualizar un diccionario

update modifica el diccionario en su lugar: las claves nuevas se añaden y las coincidentes se sobrescriben.

config = {"debug": False, "idioma": "es"}
config.update({"debug": True, "tema": "oscuro"})
print(config)   # {'debug': True, 'idioma': 'es', 'tema': 'oscuro'}

defaultdict: diccionario con valor por defecto

collections.defaultdict es un subtipo de dict que, al acceder a una clave inexistente, crea el valor mediante una función de fábrica. El código de recuentos y agrupaciones se vuelve más corto.

from collections import defaultdict

gastos = defaultdict(int)
for categoria, importe in [("Comida", 250), ("Taxi", 300), ("Comida", 400)]:
    gastos[categoria] += importe
print(dict(gastos))   # {'Comida': 650, 'Taxi': 300}

grupos = defaultdict(list)
grupos["Comida"].append("café")

Para contar apariciones existe además collections.Counter: Counter(["a", "b", "a"]) devuelve Counter({'a': 2, 'b': 1}).

Recorrer un diccionario con for

Al recorrer un diccionario con for, por defecto se obtienen las claves. Para obtener a la vez la clave y el valor, recorra items(); es la variante más habitual.

gastos = {"Comida": 650, "Taxi": 300}

for categoria in gastos:              # claves
    print(categoria)

for importe in gastos.values():       # valores
    print(importe)

for categoria, importe in gastos.items():   # pares
    print(f"{categoria}: {importe} €")

No se puede cambiar el tamaño del diccionario mientras se recorre: aparecerá RuntimeError: dictionary changed size during iteration. Si hay que eliminar algunas claves, recorra una copia: for k in list(d):.

Mostrar un diccionario en pantalla

print(d) muestra el diccionario en una sola línea. Para diccionarios grandes o anidados resulta mejor el módulo pprint o json.dumps(d, ensure_ascii=False, indent=2), que muestra los datos con sangría y sin escapar los caracteres acentuados.

Ordenar un diccionario

El diccionario no se ordena en su lugar: no tiene método sort. Ordenar un diccionario significa crear otro a partir de sus pares ordenados con sorted.

gastos = {"Taxi": 300, "Comida": 650, "Cine": 120}

por_clave = dict(sorted(gastos.items()))
print(por_clave)    # {'Comida': 650, 'Cine': 120, 'Taxi': 300}

por_valor = dict(sorted(gastos.items(), key=lambda p: p[1], reverse=True))
print(por_valor)    # {'Comida': 650, 'Taxi': 300, 'Cine': 120}

Si solo necesita las N categorías principales, no vuelva a convertir el resultado en diccionario: sorted(gastos.items(), key=lambda p: p[1], reverse=True)[:2] devuelve una lista de pares.

Para ordenar una lista de diccionarios por un campo se escribe sorted(usuarios, key=lambda u: u["edad"]).

Unir y sumar diccionarios

Desde Python 3.9, los diccionarios se unen con el operador |, y |= actualiza el diccionario en su lugar. Si las claves coinciden, gana el operando de la derecha.

a = {"x": 1, "y": 2}
b = {"y": 20, "z": 3}
print(a | b)          # {'x': 1, 'y': 20, 'z': 3}
print({**a, **b})     # lo mismo mediante desempaquetado
a |= b                # como a.update(b)

Tenga en cuenta que a + b no funciona con diccionarios: da TypeError. Si lo que quiere es sumar los valores de las claves coincidentes, use Counter: Counter(a) + Counter(b).

Desempaquetado de diccionarios

Dos asteriscos ** desempaquetan un diccionario en argumentos con nombre de una función o en otro diccionario:

def saludar(nombre, ciudad):
    return f"{nombre} de {ciudad}"

datos = {"nombre": "Ana", "ciudad": "Valencia"}
print(saludar(**datos))   # Ana de Valencia

Comprensión de diccionarios (dict comprehension)

Una comprensión de diccionarios construye un diccionario en una línea a partir de cualquier secuencia:

cuadrados = {n: n * n for n in range(5)}
print(cuadrados)   # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

grandes = {k: v for k, v in gastos.items() if v > 200}

invertido = {v: k for k, v in {"a": 1, "b": 2}.items()}
print(invertido)   # {1: 'a', 2: 'b'}

El último ejemplo muestra cómo invertir un diccionario, intercambiando claves y valores. Funciona si los valores son únicos y hashables; de lo contrario, algunos pares se pierden.

Diccionarios anidados

Un valor puede ser otro diccionario. Así se describen estructuras como las respuestas JSON de una API o un diccionario de diccionarios de usuarios.

usuarios = {
    101: {"nombre": "Ana", "habilidades": ["python", "sql"]},
    102: {"nombre": "Óscar", "habilidades": ["go"]},
}
print(usuarios[101]["habilidades"][0])            # python
print(usuarios.get(103, {}).get("nombre"))        # None, sin KeyError

Copiar un diccionario: copia y segundo nombre

La sentencia b = a no copia el diccionario: pone un segundo nombre al mismo objeto. Si cambia algo a través de b, el cambio se ve también en a. Lo mismo ocurre al pasar un diccionario a una función: la función recibe una referencia al objeto compartido y puede estropearlo sin avisar.

import copy

a = {"limites": [0, 500]}
b = a.copy()              # copia superficial
b["limites"].append(1000) # ¡la lista anidada es común!
print(a)                  # {'limites': [0, 500, 1000]}

c = copy.deepcopy(a)      # copia completamente independiente

Comparar diccionarios

El operador == compara los diccionarios por su contenido: son iguales si coinciden todos los pares. El orden de las claves no cuenta: {"a": 1, "b": 2} == {"b": 2, "a": 1} da True. Para encontrar las claves comunes, use las operaciones de conjuntos sobre las vistas: a.keys() & b.keys().

Diccionario tipado: anotaciones

La anotación de un diccionario se escribe como dict[str, int]. Si el diccionario tiene un conjunto fijo de campos de distintos tipos, resulta cómodo TypedDict del módulo typing:

from typing import TypedDict

class Usuario(TypedDict):
    nombre: str
    edad: int

u: Usuario = {"nombre": "Ana", "edad": 30}

Las anotaciones no se comprueban durante la ejecución: las leen los IDE y los analizadores como mypy.

Diccionario inmutable: frozendict en Python 3.15

En Python 3.15 apareció el tipo integrado frozendict (PEP 814): no hace falta importar nada, y cualquier intento de modificarlo termina de inmediato con TypeError. Antes, un diccionario inmutable había que simularlo con un envoltorio del módulo types o con un paquete externo. Es justo lo que se necesita para la configuración común que ninguna función debería cambiar en silencio.

config = frozendict(importe_minimo=0, importe_maximo=500)
config["importe_maximo"] = 1000
# TypeError: 'frozendict' object does not support item assignment

estricto = config | {"importe_maximo": 300}   # nuevo frozendict, el original no cambia
cache = {config: "informe de mayo"}           # es hashable: sirve como clave

Lo que conviene saber, según el material del curso:

  • frozendict hereda de object, no de dict, así que isinstance(cfg, dict) devuelve False. Para comprobar «cualquier diccionario» use collections.abc.Mapping o isinstance(x, (dict, frozendict)).
  • Es hashable solo si lo son sus claves y valores: frozendict(a=[1]) se crea, pero hash() falla. La inmutabilidad es superficial, como la de una tupla.
  • El orden se conserva, pero == no lo tiene en cuenta.
  • json.dumps, pickle y copy admiten frozendict.

Errores típicos

  • KeyError al leer: use get o una comprobación previa con in.
  • Una lista como clave: conviértala en tupla, d[tuple(lista)].
  • Modificar el diccionario dentro de un bucle: recorra list(d).
  • Esperar una copia al asignar: use copy() o deepcopy.
  • {} en lugar de un conjunto vacío: es un diccionario vacío.

Resumen

El dict de Python es la estructura de datos principal del lenguaje: búsqueda rápida por clave, orden de inserción conservado, métodos prácticos como get, setdefault y update, recorrido con items(), ordenación con sorted y unión con el operador |. Tenga en cuenta las copias y las claves hashables, y para la configuración en Python 3.15 use frozendict. Los diccionarios, las listas, las tuplas y los conjuntos se practican en el curso «Python 3.15: de principiante a profesional» con un registro de gastos.

Preguntas frecuentes

¿Se pueden guardar en un diccionario valores de tipos distintos?

Sí, los valores pueden ser de cualquier tipo y mezclarse: números, cadenas, listas, otros diccionarios. La única restricción es para las claves, que deben ser hashables.

¿Cómo saber cuántos elementos tiene un diccionario?

La función len(d) devuelve el número de pares clave — valor. Para un diccionario vacío devuelve 0, y en una condición if el diccionario vacío se considera falso.

¿En qué se diferencia dict de defaultdict?

defaultdict es un subtipo de dict que, al acceder a una clave inexistente, no lanza KeyError sino que crea el valor con una fábrica, por ejemplo int o list. En todo lo demás se comporta como un diccionario normal.

¿Cómo obtener una clave a partir de un valor?

No hay un método directo; hay que recorrer el diccionario: next((k for k, v in d.items() if v == valor), None). Si esta búsqueda se hace a menudo, conviene construir el diccionario inverso con {v: k for k, v in d.items()}.

¿Cómo guardar un diccionario en un archivo?

Lo habitual es el módulo json: json.dump(d, f, ensure_ascii=False, indent=2) escribe en un archivo abierto para escritura, y json.load(f) lo lee. Las claves se convierten en cadenas.

Aprende este tema en vídeo

Este tema se explica a fondo y con práctica en el curso «Python 3.15: от новичка до профи».

Ir al curso