Diccionarios
Las listas te permiten buscar cosas por posición. Pero muchas veces quieres buscar algo por nombre. No es "dame el elemento 3", sino "dame el puntaje de Alicia". Un diccionario guarda datos como pares clave-valor: buscas un valor por su clave, no por su posición.
Crear un diccionario
Llaves con dos puntos entre cada clave y su valor, y comas entre pares. Las claves casi siempre son strings. Los valores pueden ser cualquier cosa: números, strings, otras listas, incluso otros diccionarios.
jugador = {
"nombre": "Alicia",
"puntaje": 87,
"nivel": 5,
"vivo": True,
}Acceder a los valores
Usa corchetes con la clave para obtener el valor. Si la clave no existe, Python lanza un KeyError. Usa .get() cuando no estés seguro de que una clave está presente: devuelve None en lugar de fallar, o un valor por defecto que tú especifiques.
jugador = {"nombre": "Alicia", "puntaje": 87}
jugador["nombre"] # "Alicia"
jugador["puntaje"] # 87
jugador["vidas"] # KeyError (la clave no existe)jugador.get("puntaje") # 87
jugador.get("vidas") # None (sin error, devuelve None por defecto)
jugador.get("vidas", 3) # 3 (usa este valor por defecto si la clave no está).get() es más seguro siempre que una clave pueda faltar:
cantidad = inventario.get("flechas", 0) # 0 si "flechas" no está en el dictd["clave"] te entrega el valor, pero lanza KeyError si esa clave no existe. Cuando no estés seguro, recurre a .get(): devuelve None en lugar de fallar, o un valor por defecto que tú pases. Ahora le pongo un valor por defecto a todo y mis programas dejaron de fallar por claves faltantes. Agregar y actualizar
Asigna a una clave usando corchetes. Si la clave ya existe, se reemplaza el valor. Si no existe todavía, se crea una entrada nueva. Usa .update() para combinar todo un diccionario dentro de otro de una sola vez.
jugador = {"nombre": "Alicia", "puntaje": 87}
jugador["puntaje"] = 92 # actualiza uno existente
jugador["nivel"] = 5 # agrega una clave nuevaextras = {"nivel": 5, "vivo": True}
jugador.update(extras) # agrega/sobrescribe con las claves de extras.update() para incorporar todo un diccionario de una sola vez. Eliminar elementos
Hay cuatro formas de eliminar entradas. .pop() elimina una clave y te devuelve su valor. .pop() con un valor por defecto es seguro cuando la clave podría no estar. del elimina una clave sin devolver nada. .clear() vacía todo el diccionario.
jugador = {"nombre": "Alicia", "puntaje": 87, "nivel": 5}
jugador.pop("nivel") # elimina "nivel" y devuelve 5
jugador.pop("vidas", None) # pop seguro, devuelve None si la clave no está
del jugador["puntaje"] # elimina "puntaje", sin valor de retorno
jugador.clear() # elimina todo.pop() con un valor por defecto es la forma más segura de eliminar una clave que podría no existir.
.pop(key) elimina una clave y te entrega su valor de vuelta, y .pop(key, None) se mantiene tranquilo cuando la clave podría no estar. del d[key] elimina sin devolver nada, .clear() vacía todo. El valor por defecto en .pop() me salvó de muchas sorpresas con KeyError. Iterar
Tres vistas te permiten recorrer distintas partes de un diccionario. Iterar el dict directamente te da las claves. .values() te da los valores. .items() te da ambos a la vez y es lo que más usarás: desempaqueta cada par en dos nombres para tener bucles limpios y legibles.
jugador = {"nombre": "Alicia", "puntaje": 87, "nivel": 5}
for clave in jugador: # itera las claves (lo más común)
print(clave)
for clave in jugador.keys(): # lo mismo, con la vista explícita de claves
print(clave)
for valor in jugador.values(): # los valores
print(valor)
for clave, valor in jugador.items(): # ambos, lo más útil
print(f"{clave}: {valor}").items() es lo que más usarás. Desempaquetar cada par en dos nombres hace que el bucle sea más legible.
.values() te da los valores, y .items() te da ambos a la vez, que es lo que más usarás. Desempaquetar cada par en dos nombres como for key, value in player.items() hizo que mis bucles fueran mucho más fáciles de leer. Verificar la pertenencia
in verifica si una clave existe en el diccionario. No verifica los valores, solo las claves. Para verificar si algo no está presente, usa not in.
jugador = {"nombre": "Alicia", "puntaje": 87}
"nombre" in jugador # True
"vidas" in jugador # False
"vidas" not in jugador # Truein solo verifica claves. Para verificar valores, usa in jugador.values(), aunque rara vez se necesita.
in te dice si una clave está en el dict, y solo una clave, nunca un valor. Se mantiene rápido sin importar cuán grande sea el dict. Cámbialo a not in para verificar que algo esté ausente. Diccionarios anidados
Los valores pueden ser diccionarios en sí mismos. Así es como representas datos estructurados con varios niveles: un jugador que tiene una sección de estadísticas, un archivo de configuración con subsecciones. Dos pares de corchetes acceden a un valor anidado: el primero elige la clave externa, el segundo elige la clave interna.
usuarios = {
"alicia": {"puntaje": 87, "nivel": 5},
"beto": {"puntaje": 74, "nivel": 3},
}
usuarios["alicia"]["puntaje"] # 87
usuarios["beto"]["nivel"] # 3Se accede con corchetes encadenados. Para estructuras muy anidadas, esto puede volverse poco manejable, así que mantén la anidación superficial cuando puedas.
setdefault
.setdefault() lee una clave si existe, o la fija a un valor por defecto si no existe, y luego devuelve el valor. Es útil cuando necesitas que una clave exista pero no quieres sobrescribirla si ya está ahí.
inventario = {}
inventario.setdefault("flechas", 0) # fija "flechas": 0, devuelve 0
inventario.setdefault("flechas", 10) # "flechas" ya existe, no cambia, devuelve 0Es útil para construir estructuras agrupadas sin verificar antes si la clave existe:
grupos = {}
for nombre, equipo in jugadores:
grupos.setdefault(equipo, []).append(nombre).setdefault(key, default) lee una clave si está ahí, o la fija al valor por defecto y lo devuelve si no lo está. Es la forma ordenada de asegurar que una clave exista sin arruinar un valor que ya estaba ahí. Lo uso más que nada para agrupar cosas, sin necesidad de verificar "¿ya está esta clave?". collections.defaultdict y Counter
La biblioteca estándar tiene dos subclases de dict que manejan patrones comunes automáticamente. defaultdict crea un valor por defecto para las claves faltantes para que nunca obtengas un KeyError. Counter cuenta cuántas veces aparece cada elemento en una secuencia y te da los resultados como un dict.
defaultdict y Counter viven en la biblioteca estándar, así que hay que importarlas primero. Los imports reciben tratamiento completo en el capítulo de Módulos.
from collections import defaultdict
grupos = defaultdict(list)
for nombre, equipo in jugadores:
grupos[equipo].append(nombre) # sin KeyError aunque equipo sea nuevofrom collections import Counter
palabras = ["gato", "perro", "gato", "pajaro", "gato", "perro"]
conteos = Counter(palabras)
# Counter({'gato': 3, 'perro': 2, 'pajaro': 1})
conteos.most_common(2) # [('gato', 3), ('perro', 2)]Counter ahorra mucho código repetitivo de "contar cosas en un bucle".
defaultdict rellena un valor por defecto para cualquier clave faltante, así que agrupar y contar dejan de lanzarte KeyError. Counter cuenta cuántas veces aparece cada elemento en una secuencia y te lo devuelve como un dict. La primera vez que cambié un bucle de conteo escrito a mano por Counter, la mitad de mi código desapareció. En la práctica
Construyendo un rastreador de puntajes e imprimiendo un resumen con todas las entradas:
puntajes = {"Alicia": 87, "Beto": 74, "Carla": 92, "Diego": 55}
total = sum(puntajes.values())
promedio = total / len(puntajes)
print(f"Jugadores: {len(puntajes)}")
print(f"Promedio: {promedio:.1f}")
print(f"Mayor: {max(puntajes.values())}")
print(f"Menor: {min(puntajes.values())}")
print()
for nombre, puntaje in puntajes.items():
print(f" {nombre}: {puntaje}")
