dict y set
En resumen: Un mapa (diccionario) guarda pares de clave y valor con claves únicas y encuentra rápido un valor por su clave, y un conjunto guarda valores únicos.
Problema
¿Quién es este usuario?
Los usuarios del sitio están en una lista. En cada inicio de sesión buscas el correo por el nombre de usuario:
def find_email(users, username): for u in users: if u.username == username: return u.email return NoneCon tres usuarios es instantáneo. Con 2 000 000, cada inicio de sesión compara hasta 2 000 000 nombres, y hay miles de inicios de sesión por minuto. Una guía telefónica no se lee desde la primera página. Vas directo a la letra.
La idea
Diccionario: clave → valor
Quieres preguntar «¿cuál es el correo de garcia?» y obtener la respuesta al instante. La colección para esto se llama diccionario (mapa). Guarda pares: una clave y un valor. En Python el diccionario viene incorporado y se llama dict:
emails = {}emails["garcia"] = "garcia@example.com"emails["mlopez"] = "mlopez@example.com"emails["fernandez"] = "fernandez@example.com"
print(emails["fernandez"])print(emails.get("nadie"))print("Usuarios:", len(emails))fernandez@example.comNoneUsuarios: 3{}es un diccionario vacío.emails[clave] = valorguarda un par, yemails[clave]devuelve el valor.get(clave)también devuelve el valor, y para una clave que no está daNone.len(emails)es el número de pares. No necesitas unimport.- No escribes tipos: un mismo diccionario puede tener cualquier clave y cualquier valor. Si quieres decir cuáles son, hay una anotación de tipo:
emails: dict[str, str] = {}.
emails[clave] para una clave que no está no da None. Detiene el programa. También puedes escribir un diccionario de una vez, con pares clave: valor:
emails = {"garcia": "garcia@example.com", "mlopez": "mlopez@example.com"}print(emails["garcia"])try: print(emails["nadie"])except KeyError as e: print(f"KeyError: {e}")garcia@example.comKeyError: 'nadie'emails[clave] no recorre todos los pares. Encuentra el lugar de la clave casi al instante, sin importar cuántos haya. Cómo lo hace es la siguiente lección.
La idea
El almacén: cambios y comprobaciones
Las existencias de un almacén: artículo → cantidad.
stock = {}stock["portátil"] = 4stock["ratón"] = 25stock["portátil"] = 3
print(stock["portátil"])print("monitor" in stock)print(stock.get("monitor", 0))del stock["ratón"]print(len(stock))3False01- Las claves son únicas. El segundo
stock["portátil"] = 3no agrega una fila nueva. Reemplaza 4 por 3. clave in stockpregunta si la clave está en el diccionario.get(clave, 0)devuelve el valor, o 0 si la clave no está. Esto te protege deKeyError.del stock[clave]quita el par.
Más precisamente, para curiosos
- Los valores se pueden repetir: el portátil y el ratón pueden ser 3 los dos. Las claves no.
-
También puedes preguntar
3 in stock.values(). Pero eso recorre todos los valores, porque un diccionario es rápido solo por clave.
Pruébalo
¿Cuántos hay en el menú?
¿Qué imprimirá el programa?
menu = {}menu["café"] = 2menu["té"] = 2menu["café"] = 3print(len(menu), menu["café"])La segunda escritura, menu["café"] = 3, reemplaza el precio del café. Sigue habiendo dos claves, café y té, y el café ahora cuesta 3.
La idea
Recorrido y orden
El precio de envío por ciudad. items() da los pares de clave y valor, y for city, price in separa cada uno en dos nombres:
delivery = {}delivery["Sofia"] = 4.5delivery["Plovdiv"] = 5.2delivery["Varna"] = 6.0delivery["Burgas"] = 6.0delivery["Ruse"] = 5.8
for city, price in delivery.items(): print(f"{city}: {price:.2f} €")Sofia: 4.50 €Plovdiv: 5.20 €Varna: 6.00 €Burgas: 6.00 €Ruse: 5.80 €Las ciudades salen en el orden en que se agregaron. dict mantiene el orden en que se agregan, y desde Python 3.7 eso está prometido. :.2f imprime el precio con dos cifras después del punto decimal. Con las mismas asignaciones y print(delivery):
delivery = {}delivery["Sofia"] = 4.5delivery["Plovdiv"] = 5.2delivery["Varna"] = 6.0delivery["Burgas"] = 6.0delivery["Ruse"] = 5.8print(delivery){'Sofia': 4.5, 'Plovdiv': 5.2, 'Varna': 6.0, 'Burgas': 6.0, 'Ruse': 5.8}Cuando necesitas el orden alfabético, sorted(delivery) da una lista ordenada de las claves. El diccionario no cambia:
delivery = {}delivery["Sofia"] = 4.5delivery["Plovdiv"] = 5.2delivery["Varna"] = 6.0delivery["Burgas"] = 6.0delivery["Ruse"] = 5.8
for city in sorted(delivery): print(f"{city}: {delivery[city]:.2f} €")Burgas: 6.00 €Plovdiv: 5.20 €Ruse: 5.80 €Sofia: 4.50 €Varna: 6.00 €Más precisamente, para curiosos
- Si necesitas solo las claves, recorre
delivery. Si necesitas solo los valores, recorrevalues():
for city in delivery: print(city, end=" ")print()
print(f"Media: {sum(delivery.values()) / len(delivery):.2f} €")Sofia Plovdiv Varna Burgas RuseMedia: 5.50 €sum(...)suma los números, ylen(delivery)es el número de pares.- El diccionario mantiene el orden en que se agregan, pero el
setdel final de la lección no.
La idea
Conteo
¿Cuántas veces se pidió cada artículo? La clave es el artículo, y el valor es la cantidad. En cada pedido tomas la cantidad anterior (o 0) y le sumas 1:
orders = "café té café cacao café té"counts = {}for item in orders.split(" "): counts[item] = counts.get(item, 0) + 1print(counts){'café': 3, 'té': 2, 'cacao': 1}Este es el uso más común de un diccionario: contar palabras, votos, errores por tipo, ventas por ciudad. Los artículos salen en el orden en que aparecieron por primera vez.
orders = "café té café cacao café té"counts = {}for item in orders.split(" "): counts[item] = counts.get(item, 0) + 1print(counts)Paso a paso, en palabras
- 6 pedidos y un diccionario vacío.
- «café» no está: get(item, 0) da 0, y la asignación añade la clave con 1.
- «té» no está: get(item, 0) da 0, y la asignación añade la clave con 1. El diccionario guarda el orden en que se añaden: la clave nueva es la última.
- «café» ya está: get da 1, y la asignación reemplaza el valor por 2.
- «cacao» no está: get(item, 0) da 0, y la asignación añade la clave con 1. El diccionario guarda el orden en que se añaden: la clave nueva es la última.
- «café» ya está: get da 2, y la asignación reemplaza el valor por 3.
- «té» ya está: get da 1, y la asignación reemplaza el valor por 2.
- Cada producto es una clave una sola vez, y el valor es cuántas veces se pidió.
Más precisamente, para curiosos
split(" ")de la lección sobre texto divide la línea en palabras.-
Contar es tan común que
collectionstiene un diccionario listo para eso,Counter. Para una clave que no está, da 0, noKeyError:
from collections import Counter
orders = "café té café cacao café té"counts = Counter(orders.split(" "))print(counts)print(counts["café"], counts["jugo"])Counter({'café': 3, 'té': 2, 'cacao': 1})3 0Pruébalo
Encuentra el error
KeyError: 'café'¿En qué línea está el error?
Elige la línea con el error.
counts[item] detiene el programa con KeyError. El mensaje dice qué clave falta. La corrección es counts.get(item, 0) + 1.La idea
set: cada uno una vez
¿Cuántas personas distintas iniciaron sesión hoy en el sitio? Si alguien entra cinco veces, debe contarse una sola. Una colección que guarda cada valor una sola vez se llama conjunto:
visits = ["ana@example.com", "bruno@example.com", "ana@example.com", "vera@example.com", "bruno@example.com"]visitors = set()for email in visits: visitors.add(email)print("Visitas:", len(visits))print("Visitantes únicos:", len(visitors))print("vera@example.com" in visitors)Visitas: 5Visitantes únicos: 3Trueset()es un conjunto vacío.{}no lo es: es un diccionario vacío.addsobre un valor que ya está no hace nada. En los dos casos devuelveNone, así que no te dice si agregó algo:
seen = set()print(seen.add("F-101"))Nonevalor in visitorses tan rápido comoinen un diccionario. Por dentro, un conjunto es como un diccionario en el que guardas solo las claves.- No hay índices. Un conjunto responde solo a la pregunta «¿está aquí?».
Más precisamente, para curiosos
- Un conjunto no mantiene ningún orden. Con texto, el orden cambia incluso entre dos ejecuciones del mismo programa, porque Python calcula el hash del texto de forma distinta en cada ejecución. Por eso se imprime con
sorted(). También puedes escribir un conjunto de una vez, entre{}con valores:
tags = {"java", "sql", "git", "java"}print(len(tags))print(sorted(tags))
backend = {"java", "sql", "docker"}print(sorted(tags & backend))print(sorted(tags | backend))3['git', 'java', 'sql']['java', 'sql']['docker', 'git', 'java', 'sql']&deja solo los elementos que dos conjuntos tienen en común, y|los une. Los necesitarás en el ejercicio.
Pruébalo
Una factura repetida
invoices = ["F-101", "F-102", "F-101", "F-103"]
seen = set()
for number in invoices:
if number seen:
print("Factura repetida:", number)
seen.add(number)number in seen pregunta si el número ya se vio. add no dice nada, así que primero preguntas y después recuerdas. El programa imprime «Factura repetida: F-101».Ejercicio
Diccionarios en acción
Resuelve las tareas en tu editor.
1. Una tabla de usuarios. Una clase UserTable con los métodos add(username, email), get(username) y size(). Por dentro es un diccionario. Agrega garcia, mlopez y fernandez con correos de la forma garcia@example.com, y busca mlopez y nadie. En la siguiente lección también construirás tu propia tabla hash, sin dict.
mlopez: mlopez@example.comnadie: no existe ese usuarioUsuarios: 32. Frecuencia de palabras. Lee una línea de texto, pásala a minúsculas y cuenta cada palabra. Imprime las palabras en orden alfabético.
Café con leche y café sin azúcar y caféazúcar: 1café: 3con: 1leche: 1sin: 1y: 23. Documentos por palabra clave. Una clase DocumentIndex con los métodos add_document(doc_id, *keywords) y search(keyword), que devuelve una lista con los números de los documentos que tienen esa palabra. Por dentro es un diccionario de listas: para cada palabra, una lista de documentos. Agrega el documento 1 con "POO" y "Java", el documento 2 con "SQL" y "bases de datos", y el documento 3 con "Java" y "colecciones". Busca "Java", "SQL" y "Python".
Java: [1, 3]SQL: [2]Python: []4. Una red de contactos. Una clase Contact con nombre y correo, y una clase ContactNetwork con los métodos add_contact(name, email), add_connection(a, b) (la conexión va en los dos sentidos), friends_of(name), common(a, b) (amigos en común) y email_of(name). Guarda los contactos en un diccionario nombre → Contact, y las conexiones en un diccionario nombre → conjunto de nombres. friends_of y common devuelven una lista ordenada. Conecta a Juan con María y a Juan con Pedro.
Juan: ['María', 'Pedro']María: ['Juan']Amigos en común de María y Pedro: ['Juan']Correo de Pedro: pedro@example.comMás tareas
- Un ID único. Una función
generate_user_id(email)da a cada correo nuevo un número,U001,U002y así sucesivamente, y para un correo que ya vio devuelve el mismo número. Las mayúsculas y las minúsculas del correo cuentan igual. En la siguiente lección hay una versión con una función hash. - Un catálogo de libros. La clave es el ISBN, por ejemplo
978-84-376-0494-7, y el valor es el título. Agrega libros, busca uno e imprímelos todos, ordenados por ISBN. - Un planificador de viajes. Para cada destino, guarda una lista de actividades: «París» → «la torre Eiffel», «el Louvre». Imprime el plan en el orden en que se agregaron los destinos. ¿Necesitas algo más que un diccionario simple?
- Un diccionario en línea. Palabra → definición. Lee palabras hasta que llegue «fin». Para una palabra desconocida, imprime «No existe esa palabra.».
- Notas por asignatura. Lee líneas como «Matemáticas 6», «Física 5», «Matemáticas 4», e imprime la nota media de cada asignatura, ordenadas por nombre.
Test
Ponte a prueba
Lecciones relacionadas
Sección «Lecciones relacionadas»- Se basa en: Interfaces como clases abstractas · Excepciones · Cadenas · Listas: append, remove y sort
- Continúa en: Códigos hash y funciones hash · Clases anidadas y datos · Lambdas · Listas por comprensión y generadores
- Ejercicios sobre el tema: Colecciones