Diccionarios en Python (dict): creación, métodos, recorrido, ordenación y frozendict
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étodo | Qué 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:
frozendicthereda deobject, no dedict, así queisinstance(cfg, dict)devuelveFalse. Para comprobar «cualquier diccionario» usecollections.abc.Mappingoisinstance(x, (dict, frozendict)).- Es hashable solo si lo son sus claves y valores:
frozendict(a=[1])se crea, perohash()falla. La inmutabilidad es superficial, como la de una tupla. - El orden se conserva, pero
==no lo tiene en cuenta. json.dumps,pickleycopyadmiten frozendict.
Errores típicos
- KeyError al leer: use
geto una comprobación previa conin. - 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()odeepcopy. {}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.
Este tema se explica a fondo y con práctica en el curso «Python 3.15: от новичка до профи».
Ir al curso