Saltar al contenido
diccionarios.py · devschool

Diccionarios en Python

Lección 9 de 19 · 9 min de lectura · Actualizado el

En esta lección
  1. Crear un diccionario
  2. Leer valores
  3. Añadir, cambiar y borrar
  4. Recorrer un diccionario
  5. Contar cosas con un diccionario
  6. Fusionar diccionarios con |
  7. Qué puede ser clave
  8. Diccionarios anidados
  9. Listas de diccionarios
  10. Diccionarios y JSON
  11. Errores frecuentes
  12. Resumen

Un diccionario guarda datos en parejas de clave y valor. En lugar de acceder por posición, como en las listas, accedes por nombre. Es el equivalente a los objetos de JavaScript o los mapas de Java, y una de las estructuras que más usarás en Python. En esta lección aprenderás a crear diccionarios, leerlos sin errores, modificarlos, recorrerlos, anidarlos y usarlos para contar cosas.

Crear un diccionario

Las parejas clave: valor van entre llaves y separadas por comas:

alumno = {
    "nombre": "Marta",
    "edad": 21,
    "curso": "Python",
}
print(alumno)
# {'nombre': 'Marta', 'edad': 21, 'curso': 'Python'}
  • Las claves suelen ser textos, pero pueden ser números, tuplas o cualquier valor inmutable.
  • Los valores pueden ser cualquier cosa: números, textos, listas u otros diccionarios.
  • Las claves no se repiten: si escribes la misma dos veces, gana la última.
  • Desde Python 3.7, los diccionarios conservan el orden de inserción.

Otras formas de crear diccionarios:

vacio = {}
config = dict(idioma="es", tema="oscuro")
print(config)
# {'idioma': 'es', 'tema': 'oscuro'}

Leer valores

print(alumno["nombre"])   # Marta
print(alumno["email"])    # KeyError: 'email'

Si la clave no existe, los corchetes lanzan KeyError. Cuando no estás seguro de que la clave exista, usa get: devuelve None, o el valor por defecto que le digas, en lugar de dar error.

print(alumno.get("email"))                 # None
print(alumno.get("email", "sin email"))    # sin email
print(alumno.get("edad", 0))               # 21 (existe, así que devuelve su valor)

¿Cuándo usar cada uno? Corchetes cuando la clave debe existir (si falta, es un error del programa y conviene enterarse); get cuando es normal que falte.

Comprobar si existe una clave: in

print("edad" in alumno)       # True
print("Marta" in alumno)      # False: in mira las claves, no los valores
print("Marta" in alumno.values())   # True

Añadir, cambiar y borrar

alumno["edad"] = 22             # cambia un valor existente
alumno["email"] = "m@ej.com"    # si la clave no existe, la crea
del alumno["curso"]             # borra una clave
print(alumno)
print(len(alumno))              # número de parejas
# {'nombre': 'Marta', 'edad': 22, 'email': 'm@ej.com'}
# 3

pop: borrar y recuperar el valor

pop quita una clave y devuelve su valor. Admite un valor por defecto para no fallar si la clave no está:

carrito = {"pan": 2, "leche": 1, "huevos": 12}
unidades = carrito.pop("leche")
print(unidades)                  # 1
print(carrito)                   # {'pan': 2, 'huevos': 12}
print(carrito.pop("azucar", 0))  # 0 (no existía)

del y pop sin valor por defecto lanzan KeyError si la clave no existe.

update: modificar varias claves a la vez

perfil = {"nombre": "Luis", "ciudad": "Bilbao"}
perfil.update({"ciudad": "Madrid", "edad": 30})
print(perfil)
# {'nombre': 'Luis', 'ciudad': 'Madrid', 'edad': 30}

Las claves que ya existen se sobrescriben y las nuevas se añaden.

setdefault: leer o crear si no existe

setdefault(clave, valor) devuelve el valor de la clave si existe; si no, la crea con ese valor y lo devuelve. Es muy útil para agrupar:

alumnos = [("Ana", "DAW"), ("Luis", "DAM"), ("Eva", "DAW")]
por_ciclo = {}
for nombre, ciclo in alumnos:
    por_ciclo.setdefault(ciclo, []).append(nombre)
print(por_ciclo)
# {'DAW': ['Ana', 'Eva'], 'DAM': ['Luis']}

La primera vez que aparece "DAW", setdefault crea una lista vacía; las siguientes, devuelve la que ya existe, y en ambos casos se añade el nombre.

Recorrer un diccionario

precios = {"café": 1.5, "tostada": 2.2, "zumo": 2.8}

for producto in precios:                  # recorre las claves
    print(producto)
# café
# tostada
# zumo

for producto, precio in precios.items():  # claves y valores a la vez
    print(f"{producto}: {precio:.2f} €")
# café: 1.50 €
# tostada: 2.20 €
# zumo: 2.80 €

Tres métodos devuelven “vistas” del diccionario:

MétodoDevuelveEjemplo de uso
keys()Las claveslist(precios.keys())
values()Los valoressum(precios.values()) da 6.5
items()Parejas (clave, valor)for k, v in precios.items():

Cuidado: no añadas ni borres claves mientras recorres el diccionario: obtendrás RuntimeError: dictionary changed size during iteration. Recorre una copia, por ejemplo for clave in list(precios):.

Recorrer en orden

Un diccionario conserva el orden de inserción, pero a menudo quieres otro. sorted te da las claves ordenadas:

for producto in sorted(precios):
    print(producto, precios[producto])
# café 1.5
# tostada 2.2
# zumo 2.8

Para ordenar por valor, pasa a sorted una key que devuelva el valor:

for producto in sorted(precios, key=precios.get, reverse=True):
    print(producto, precios[producto])
# zumo 2.8
# tostada 2.2
# café 1.5

precios.get es la función que, dada una clave, devuelve su precio. Para criterios más complejos se usan lambda, que verás en Funciones avanzadas.

Contar cosas con un diccionario

Un uso clásico: contar cuántas veces aparece cada elemento.

votos = ["gato", "perro", "gato", "pez", "gato", "perro"]
recuento = {}

for voto in votos:
    recuento[voto] = recuento.get(voto, 0) + 1

print(recuento)
# {'gato': 3, 'perro': 2, 'pez': 1}

recuento.get(voto, 0) devuelve cuántos votos lleva ese animal, o 0 si todavía no tiene ninguno.

collections.Counter

Contar es tan habitual que Python trae una herramienta hecha: Counter, un diccionario especializado en contar.

from collections import Counter

recuento = Counter(votos)
print(recuento)
print(recuento["gato"])          # 3
print(recuento["loro"])          # 0: no da KeyError
print(recuento.most_common(2))   # los 2 más votados
# Counter({'gato': 3, 'perro': 2, 'pez': 1})
# 3
# 0
# [('gato', 3), ('perro', 2)]

Funciona con cualquier secuencia: Counter("banana") cuenta letras. Hacer el recuento a mano es buen ejercicio para entender los diccionarios; en código real, usa Counter.

Fusionar diccionarios con |

Desde Python 3.9, el operador | une dos diccionarios en uno nuevo. Si una clave está en los dos, gana el de la derecha:

por_defecto = {"idioma": "es", "tema": "claro", "avisos": True}
del_usuario = {"tema": "oscuro"}

config = por_defecto | del_usuario
print(config)
# {'idioma': 'es', 'tema': 'oscuro', 'avisos': True}

Es el patrón típico de “valores por defecto que el usuario puede sobrescribir”. a |= b modifica a directamente, igual que a.update(b).

Qué puede ser clave

Las claves tienen que ser inmutables (técnicamente, hashables): str, int, float, bool, tuple… Una lista no puede serlo:

distancias = {("Madrid", "Sevilla"): 530}   # una tupla como clave: bien
print(distancias[("Madrid", "Sevilla")])
# 530

errores = {["a", "b"]: 1}
# TypeError: unhashable type: 'list'

¿Por qué? El diccionario encuentra los valores tan rápido porque calcula un número a partir de la clave (su hash). Si la clave pudiera cambiar, ese número dejaría de ser válido. Tienes más sobre tuplas como claves en Tuplas y conjuntos.

Diccionarios anidados

Un valor puede ser otro diccionario (o una lista). Así se representan datos con estructura:

tienda = {
    "nombre": "Librería Sol",
    "direccion": {"calle": "Mayor 5", "ciudad": "Burgos"},
    "libros": ["El Quijote", "Rayuela"],
}

print(tienda["direccion"]["ciudad"])   # Burgos
print(tienda["libros"][0])             # El Quijote
tienda["direccion"]["cp"] = "09001"    # añadir dentro del anidado
print(tienda["direccion"])
# {'calle': 'Mayor 5', 'ciudad': 'Burgos', 'cp': '09001'}

Para leer con seguridad en varios niveles, puedes encadenar get con un diccionario vacío por defecto: tienda.get("contacto", {}).get("telefono") devuelve None en lugar de fallar.

Listas de diccionarios

La forma más habitual de guardar datos en un programa: una lista de fichas.

alumnos = [
    {"nombre": "Ana", "nota": 8},
    {"nombre": "Luis", "nota": 4},
    {"nombre": "Eva", "nota": 6.5},
]

for a in alumnos:
    estado = "aprobado" if a["nota"] >= 5 else "suspenso"
    print(f"{a['nombre']}: {estado}")
# Ana: aprobado
# Luis: suspenso
# Eva: aprobado

aprobados = [a["nombre"] for a in alumnos if a["nota"] >= 5]
print(aprobados)
# ['Ana', 'Eva']

Fíjate en las comillas de a['nombre'] dentro de la f-string: como la f-string usa comillas dobles, dentro se usan simples.

La última línea es una comprensión de lista. También existen las comprensiones de diccionario, que crean un diccionario en una línea, como {a["nombre"]: a["nota"] for a in alumnos}. Las verás en Comprensiones.

Diccionarios y JSON

Los diccionarios tienen la misma forma que el formato JSON, que es como se intercambian datos en la web:

import json

texto = json.dumps(alumnos[0])    # diccionario → texto JSON
print(texto)
de_vuelta = json.loads(texto)     # texto JSON → diccionario
print(de_vuelta["nombre"])
# {"nombre": "Ana", "nota": 8}
# Ana

Guardar y leer JSON en ficheros lo verás en Ficheros y JSON.

Errores frecuentes

  • KeyError al leer una clave que no existe: usa get o comprueba con in.
  • Creer que in busca en los valores: busca en las claves. Para valores, in dic.values().
  • Usar una lista como clave: TypeError: unhashable type. Usa una tupla.
  • Modificar el diccionario mientras lo recorres: RuntimeError. Recorre list(dic).
  • Esperar que {} sea un conjunto vacío: es un diccionario vacío.
  • Mezclar comillas en f-strings en versiones anteriores a 3.12: f"{a["nombre"]}" falla; usa f"{a['nombre']}".

Resumen

TareaCómo
Crear{"clave": valor}, dict(clave=valor), {}
Leerd["clave"] (error si falta), d.get("clave", defecto)
Existe"clave" in d
Añadir / cambiard["clave"] = valor, d.update(otro)
Borrardel d["clave"], d.pop("clave", defecto)
Leer o creard.setdefault("clave", valor)
Recorrerfor k in d, for k, v in d.items()
Ordenarsorted(d), sorted(d, key=d.get)
Contard.get(x, 0) + 1 o Counter(lista)
Fusionara | b

Pon a prueba lo que has aprendido

[Python] ¿Cómo se obtiene el valor de "precio"?
producto = {"nombre": "Libro", "precio": 12}

[Python] ¿Qué muestra este código?
alumno = {"nombre": "Marta", "edad": 21}
print("Marta" in alumno, alumno.get("email", "-"))

[Python] ¿Qué muestra este código?
a = {"x": 1, "y": 2}
b = {"y": 5, "z": 3}
print(a | b)

¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.