W3docs

Comprensiones de Diccionarios y Conjuntos en Python

Domina las comprensiones de diccionarios, conjuntos y expresiones generadoras en Python con sintaxis clara, ejemplos reales y errores comunes.

Las comprensiones de diccionarios, las comprensiones de conjuntos y las expresiones generadoras extienden la sintaxis compacta de la comprensión de listas a otras estructuras de datos. Este capítulo explica cada forma en profundidad: sintaxis, filtrado, anidamiento, patrones del mundo real y cuándo evitarlos.

Comprensiones de Diccionarios

Una comprensión de diccionario construye un nuevo dict a partir de cualquier iterable en una sola expresión. En lugar de escribir un bucle for que llama a d[key] = value en cada iteración, describes el mapeo de forma concisa dentro de llaves.

Sintaxis

new_dict = {key_expr: value_expr for item in iterable}

Añade un filtro if opcional después del iterable:

new_dict = {key_expr: value_expr for item in iterable if condition}
ParteFunción
key_exprExpresión que produce cada clave
value_exprExpresión que produce cada valor
itemVariable de bucle — toma cada valor del iterable en orden
if conditionFiltro opcional — omite los elementos donde condition es False

Ejemplo Básico: Construir un Mapa de Cuadrados

squares = {x: x ** 2 for x in range(1, 6)}
print(squares)
# {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

El bucle for equivalente:

squares = {}
for x in range(1, 6):
    squares[x] = x ** 2

Ambos producen el mismo resultado; la forma de comprensión es más concisa y expresa la intención a simple vista.

Filtrar Entradas

Conserva solo los pares que satisfacen una condición:

prices = {"apple": 1.20, "banana": 0.50, "orange": 0.80, "grape": 2.50}

expensive = {item: price for item, price in prices.items() if price >= 1.00}
print(expensive)
# {'apple': 1.2, 'grape': 2.5}

Transformar Valores

Aplica una función o expresión aritmética a cada valor:

prices = {"apple": 1.20, "banana": 0.50, "orange": 0.80}

# Apply a 10% discount and round to 2 decimal places
discounted = {item: round(price * 0.9, 2) for item, price in prices.items()}
print(discounted)
# {'apple': 1.08, 'banana': 0.45, 'orange': 0.72}

Intercambiar Claves y Valores

Invierte un diccionario (funciona correctamente cuando todos los valores son únicos):

capitals = {"France": "Paris", "Germany": "Berlin", "Japan": "Tokyo"}

inverted = {city: country for country, city in capitals.items()}
print(inverted)
# {'Paris': 'France', 'Berlin': 'Germany', 'Tokyo': 'Japan'}

Si los valores no son únicos, las entradas posteriores sobrescriben a las anteriores. Usa esto solo cuando estés seguro de que los valores forman una correspondencia uno a uno.

Construir desde Dos Iterables con zip()

zip() empareja elementos de dos secuencias, lo que facilita la construcción de un diccionario a partir de listas separadas de claves y valores:

keys = ["name", "age", "city"]
values = ["Alice", 30, "Berlin"]

profile = {k: v for k, v in zip(keys, values)}
print(profile)
# {'name': 'Alice', 'age': 30, 'city': 'Berlin'}

Esto es equivalente a dict(zip(keys, values)), pero la forma de comprensión permite añadir fácilmente un filtro o transformar valores al mismo tiempo.

Normalizar Claves

Una tarea habitual en el mundo real es limpiar las claves de un diccionario; por ejemplo, convertir todas las claves a minúsculas al combinar datos de diferentes fuentes:

raw = {"Name": "Alice", "AGE": 30, "City": "Berlin"}

normalised = {k.lower(): v for k, v in raw.items()}
print(normalised)
# {'name': 'Alice', 'age': 30, 'city': 'Berlin'}

Expresión de Valor Condicional (Ternario en el Valor)

Puedes usar una expresión ternaria en la posición del valor para elegir entre dos valores por elemento:

scores = {"Alice": 95, "Bob": 62, "Carol": 78, "Dave": 45}

grades = {name: "pass" if score >= 70 else "fail" for name, score in scores.items()}
print(grades)
# {'Alice': 'pass', 'Bob': 'fail', 'Carol': 'pass', 'Dave': 'fail'}

Comprensiones de Diccionarios Anidadas

Puedes anidar una comprensión para manejar datos de varios niveles. Por ejemplo, extraer un campo de una lista de diccionarios anidados:

students = [
    {"name": "Alice", "score": 95, "grade": "A"},
    {"name": "Bob",   "score": 82, "grade": "B"},
    {"name": "Carol", "score": 91, "grade": "A"},
]

# Build a {name: score} mapping from the list
name_to_score = {s["name"]: s["score"] for s in students}
print(name_to_score)
# {'Alice': 95, 'Bob': 82, 'Carol': 91}

Mantén el anidamiento superficial. Si la lógica se vuelve difícil de seguir, extrae una función auxiliar o usa un bucle for simple.

Comprensiones de Conjuntos

Una comprensión de conjunto construye un set en una sola expresión. Como los conjuntos solo contienen elementos únicos, los duplicados se eliminan automáticamente.

Sintaxis

new_set = {expression for item in iterable}
new_set = {expression for item in iterable if condition}

La única diferencia visual respecto a una comprensión de diccionario es la ausencia de dos puntos: hay una sola expresión, no un par key: value.

Ejemplo Básico: Cuadrados Únicos

numbers = [1, 2, 2, 3, 3, 3, 4]
unique_squares = {x ** 2 for x in numbers}
print(unique_squares)
# {1, 4, 9, 16}

El orden de salida no está garantizado: los conjuntos no están ordenados, así que no dependas de un orden de presentación específico.

Deduplicar Mientras se Transforma

Un patrón habitual es normalizar cadenas y deduplicar en un solo paso:

tags = ["Python", "python", "PYTHON", "Data", "data", "DATA"]

unique_tags = {tag.lower() for tag in tags}
print(unique_tags)
# {'python', 'data'}

Filtrar con una Condición

words = ["cat", "elephant", "dog", "rhinoceros", "ant"]

long_words = {w for w in words if len(w) > 4}
print(long_words)
# {'elephant', 'rhinoceros'}

Encontrar Elementos Comunes

Las comprensiones de conjuntos se combinan de forma natural con las operaciones de conjuntos:

list_a = [1, 2, 3, 4, 5, 5, 6]
list_b = [4, 5, 6, 7, 8]

set_a = {x for x in list_a}
set_b = {x for x in list_b}

common = set_a & set_b
print(common)
# {4, 5, 6}

Para una conversión directa sin transformación, set(list_a) es más sencillo. Usa una comprensión de conjunto cuando necesites filtrar o transformar durante la conversión.

Expresiones Generadoras

Una expresión generadora se parece a una comprensión de listas pero usa paréntesis en lugar de corchetes. En vez de construir toda la colección en memoria de una vez, produce un valor a la vez, bajo demanda (de forma perezosa).

Sintaxis

gen = (expression for item in iterable)
gen = (expression for item in iterable if condition)

¿Por Qué Usar una Expresión Generadora?

EscenarioComprensión de listaExpresión generadora
Necesitas iterar el resultado varias vecesNo — los generadores se agotan tras un solo recorrido
Necesitas acceso aleatorio por índice (result[3])No
El resultado se pasa a sum(), max(), any(), etc.Funciona pero reserva una listaPreferida — transmite valores sin construir una lista
Secuencia muy grande o infinitaPuede quedarse sin memoriaEficiente — un valor a la vez

Ejemplo Básico

# List comprehension — builds the entire list in memory
squares_list = [x ** 2 for x in range(1, 6)]
print(squares_list)   # [1, 4, 9, 16, 25]

# Generator expression — yields values one at a time
squares_gen = (x ** 2 for x in range(1, 6))
print(squares_gen)    # <generator object <genexpr> at 0x...>
print(list(squares_gen))  # [1, 4, 9, 16, 25]

El objeto generador en sí no es la lista: lo consumes iterando sobre él o pasándolo a una función.

Pasar Directamente a Funciones Integradas

Cuando pasas una expresión generadora como único argumento a una función, puedes omitir el par de paréntesis adicional:

total = sum(x ** 2 for x in range(1, 1001))
print(total)  # 333833500

maximum = max(len(word) for word in ["apple", "banana", "kiwi"])
print(maximum)  # 6

any_negative = any(x < 0 for x in [1, -2, 3])
print(any_negative)  # True

Ventaja de Memoria

Para datos grandes, una expresión generadora evita cargar todo en RAM:

# Simulate a large log file as a list of strings
log_lines = [f"ERROR line {i}" if i % 100 == 0 else f"INFO line {i}" for i in range(1_000_000)]

# Generator scans lines without building an intermediate list
error_count = sum(1 for line in log_lines if line.startswith("ERROR"))
print(error_count)  # 10000

Los Generadores se Agotan Tras un Solo Recorrido

Este es el error más común:

gen = (x * 2 for x in range(5))

print(list(gen))  # [0, 2, 4, 6, 8]
print(list(gen))  # [] — the generator is now empty

Si necesitas iterar el resultado más de una vez, usa una comprensión de lista o vuelve a crear el generador.

Encadenar Expresiones Generadoras

Las expresiones generadoras se pueden componer sin crear listas intermedias. Cada etapa extrae valores de la anterior:

numbers = range(1, 11)

# Stage 1: filter even numbers
evens = (x for x in numbers if x % 2 == 0)

# Stage 2: square them
even_squares = (x ** 2 for x in evens)

print(list(even_squares))  # [4, 16, 36, 64, 100]

No se crea ninguna lista intermedia: los valores fluyen a través del pipeline de uno en uno.

Elegir la Forma Correcta

Necesitas…Usa
Una lista de valores transformados o filtrados[expr for item in it]
Un diccionario a partir de pares de valores{k: v for item in it}
Una colección sin duplicados{expr for item in it}
Iteración de un solo recorrido eficiente en memoria(expr for item in it)
Lógica compleja de múltiples instrucciones por elementoBucle for simple

Errores Comunes

Comprensión de diccionario vs. comprensión de conjunto. Ambas usan llaves {}. La diferencia está en si escribes key: value (dict) o una sola expression (set). Un {} vacío siempre crea un dict vacío, no un conjunto vacío: usa set() para un conjunto vacío.

d = {}     # empty dict
s = set()  # empty set — NOT {}

Claves sobrescritas. Si la expresión de clave produce duplicados, los valores posteriores sobrescriben silenciosamente a los anteriores:

data = [("a", 1), ("b", 2), ("a", 99)]
d = {k: v for k, v in data}
print(d)  # {'a': 99, 'b': 2} — first 'a' is gone

Ámbito de variables. En Python 3, la variable de bucle en cualquier comprensión es local a la comprensión y no se filtra al ámbito circundante:

x = "original"
result = {x: x.upper() for x in ["a", "b", "c"]}
print(x)  # 'original' — comprehension's x did not overwrite this

Límite de legibilidad. Si necesitas más de una condición if o la expresión es larga, un bucle for simple con nombres de variables descriptivos suele ser más claro:

# Hard to read
result = {k: v for k, v in data.items() if k.startswith("user_") if v is not None}

# Easier to read
result = {}
for k, v in data.items():
    if k.startswith("user_") and v is not None:
        result[k] = v

Temas Relacionados

Práctica

Práctica
Which of the following statements about Python comprehensions is correct?
Which of the following statements about Python comprehensions is correct?
Was this page helpful?