Saltar al contenido
🐍 Curso de Python
/

Módulo 1 · Lección 28 de 59

Diccionarios: buscar por nombre

Guardar datos emparejados con una etiqueta y acceder a ellos por esa etiqueta en vez de por su posición.

⏱ 12 min de lectura

En la lección de ordenar listas quedó un fallo a la vista: para guardar jugadores y sus puntos usábamos dos listas paralelas y las cruzábamos con index(), y en cuanto dos jugadores empataban, el informe mentía. El problema de fondo es que una lista solo sabe de posiciones, y la posición 2 no significa nada; recordar que el nombre de la posición 2 va con los puntos de la posición 2 es trabajo tuyo, y tarde o temprano se rompe.

Un diccionario guarda pares: una clave y su valor. En vez de preguntar "¿qué hay en la posición 2?", preguntas "¿cuántos puntos tiene Luis?". Es la estructura de datos más útil de Python, y una vez la tengas no habrá programa en el que no aparezca.

Crear un diccionario#

Se escribe con llaves, y dentro pares clave: valor separados por comas:

python
puntos = {"Marta": 3400, "Luis": 2100, "Ana": 1200}

print(puntos)
print(type(puntos))
print(len(puntos))
Salida en pantalla
{'Marta': 3400, 'Luis': 2100, 'Ana': 1200}
<class 'dict'>
3

len() cuenta pares, no claves ni valores por separado.

Cuando hay más de tres o cuatro pares, se escribe uno por línea, que es como lo verás casi siempre:

python
usuario = {
    "nombre": "Erwin",
    "edad": 34,
    "ciudad": "Madrid",
    "activo": True,
}

print(usuario["nombre"])
print(usuario["edad"])
Salida en pantalla
Erwin
34

Fíjate en ese segundo uso, que es distinto del primero. En puntos todas las claves eran nombres de personas y todos los valores puntuaciones: el diccionario servía como tabla de búsqueda. En usuario cada clave es un campo distinto: el diccionario sirve como registro que describe una cosa. Los dos usos son correctos y los dos son frecuentísimos.

El diccionario vacío se escribe {}, y también con dict():

python
inventario = {}

print(inventario, len(inventario), bool(inventario))
Salida en pantalla
{} 0 False

Igual que las listas y los conjuntos, un diccionario vacío es falso, así que if inventario: pregunta si tiene algo dentro.

Acceder a un valor#

Con corchetes, poniendo la clave donde en una lista pondrías el índice:

python
capitales = {
    "España": "Madrid",
    "Francia": "París",
    "Italia": "Roma",
}

print(capitales["Francia"])
print(capitales["Italia"].upper())
Salida en pantalla
París
ROMA

La clave no tiene nada que ver con el orden: capitales["Francia"] funciona igual esté donde esté. Y a diferencia de la búsqueda en una lista, el acceso es inmediato aunque el diccionario tenga un millón de entradas, por el mismo motivo que hacía rápidos a los conjuntos.

in comprueba las claves#

El operador in sobre un diccionario mira las claves, no los valores. Es lo más importante que hay que recordar de esta lección después del KeyError:

python
capitales = {"España": "Madrid", "Francia": "París"}

print("España" in capitales)
print("Madrid" in capitales)             # es un valor, no una clave
print("Madrid" in capitales.values())    # así sí
Salida en pantalla
True
False
True

Puede parecer arbitrario, pero es lo coherente: la clave es la forma de encontrar las cosas, así que preguntar "¿está en el diccionario?" significa "¿tengo una entrada con ese nombre?".

Con eso, el acceso seguro se escribe así:

python
capitales = {"España": "Madrid", "Francia": "París"}
pais = "Portugal"

if pais in capitales:
        print(f"La capital de {pais} es {capitales[pais]}")
else:
        print(f"No tengo la capital de {pais}")
Salida en pantalla
No tengo la capital de Portugal

get(): acceder sin miedo#

get() hace lo mismo que los corchetes, pero cuando la clave no existe devuelve None en vez de fallar:

python
capitales = {"España": "Madrid", "Francia": "París"}

print(capitales.get("España"))
print(capitales.get("Portugal"))
Salida en pantalla
Madrid
None

Y acepta un segundo argumento con el valor que quieres recibir cuando no hay nada:

python
capitales = {"España": "Madrid", "Francia": "París"}

print(capitales.get("Portugal", "desconocida"))
print(capitales.get("España", "desconocida"))
Salida en pantalla
desconocida
Madrid

Ese valor por defecto es lo que convierte a get() en la opción preferente. Compara las dos formas de contar algo que puede no estar:

python
existencias = {"teclado": 4, "ratón": 0}

# con corchetes hay que protegerse
if "monitor" in existencias:
        cantidad = existencias["monitor"]
else:
        cantidad = 0

# con get sale en una línea
cantidad = existencias.get("monitor", 0)

print(f"Monitores: {cantidad}")
print(f"Teclados:  {existencias.get('teclado', 0)}")
Salida en pantalla
Monitores: 0
Teclados:  4

Cinco líneas convertidas en una, y sin repetir el nombre de la clave dos veces.

Qué puede ser una clave#

Una clave tiene que ser inmutable, exactamente igual que los elementos de un conjunto y por el mismo motivo: el diccionario calcula una huella de la clave para saber dónde guardarla, y si la clave cambiara por dentro, esa huella dejaría de encontrarla.

Valen cadenas, números, booleanos y tuplas:

python
mezcla = {
    "texto": "las cadenas son las claves más habituales",
    42: "los enteros también valen",
    3.14: "y los decimales, aunque se usan poco",
    True: "y los booleanos",
    ("Madrid", "Sevilla"): "y las tuplas, para claves compuestas",
}

print(mezcla[42])
print(mezcla[("Madrid", "Sevilla")])
Salida en pantalla
los enteros también valen
y las tuplas, para claves compuestas

No valen listas, diccionarios ni conjuntos:

python
distancias = {["Madrid", "Sevilla"]: 530}
Salida en pantalla
TypeError: cannot use 'list' as a dict key (unhashable type: 'list')

Los valores, en cambio, pueden ser cualquier cosa, sin restricción: números, textos, listas, otros diccionarios. Esa asimetría es la que da tanto juego a los diccionarios, y la explotaremos en la lección de estructuras anidadas.

python
receta = {
    "nombre": "Tortilla de patatas",
    "raciones": 4,
    "ingredientes": ["patatas", "huevos", "cebolla", "aceite"],
    "vegetariana": True,
}

print(receta["ingredientes"])
print(receta["ingredientes"][0])
print(len(receta["ingredientes"]))
Salida en pantalla
['patatas', 'huevos', 'cebolla', 'aceite']
patatas
4

Fíjate en el doble corchete de la segunda línea: primero se busca la clave "ingredientes", que devuelve una lista, y después se pide el elemento 0 de esa lista. Se lee de izquierda a derecha, un paso cada vez.

Comparación con las otras colecciones#

ListaConjuntoDiccionario
Se escribe[1, 2]{1, 2}{"a": 1}
Vacío[]set(){}
Se accede porÍndiceNo se accedeClave
in miraLos elementosLos elementosLas claves
Velocidad de inLentaMuy rápidaMuy rápida
DuplicadosSíNoNo en las claves
Para quéDatos en ordenPertenencia y únicosDatos con nombre

La forma corta de decidir: si tus datos tienen una etiqueta natural (un nombre, un código, un identificador) y vas a buscar por ella, es un diccionario.

Un ejemplo completo#

agenda.py
telefonos = {
    "Ana": "600 111 222",
    "Luis": "610 333 444",
    "Marta": "620 555 666",
}

print(f"Contactos en la agenda: {len(telefonos)}")
print(f"Teléfono de Luis: {telefonos['Luis']}")

buscado = "Pedro"
print(f"Teléfono de {buscado}: {telefonos.get(buscado, 'no está en la agenda')}")

if buscado in telefonos:
        print(f"{buscado} está guardado")
else:
        print(f"{buscado} no está guardado")

# El valor puede ser una lista: varios teléfonos por contacto
telefonos_multiples = {
    "Ana": ["600 111 222", "910 000 111"],
    "Luis": ["610 333 444"],
}

print(f"Ana tiene {len(telefonos_multiples['Ana'])} teléfonos")
print(f"El primero es {telefonos_multiples['Ana'][0]}")
Salida en pantalla
Contactos en la agenda: 3
Teléfono de Luis: 610 333 444
Teléfono de Pedro: no está en la agenda
Pedro no está guardado
Ana tiene 2 teléfonos
El primero es 600 111 222