Comprensiones y Expresiones Generadoras
Las comprensiones construyen listas, diccionarios y conjuntos a partir de iterables en una sola expresión. Las expresiones generadoras se ven similares pero devuelven un iterador perezoso, ideal para canalizaciones que nunca necesitan la colección completa en memoria.
Receta
squares = [n * n for n in range(10)]
evens = {n for n in range(20) if n % 2 == 0}
index = {name: i for i, name in enumerate(["ada", "linus"])}
total = sum(x * x for x in range(100_000)) # generador - sin lista grandeCuándo usar esto:
- Transformar o filtrar secuencias de forma declarativa
- Construir diccionarios de búsqueda a partir de iterables paralelos
- Procesar grandes conjuntos de datos a través de
sum/any/all/max - Reemplazar bucles de
appendde tres líneas con una sola expresión
Ejemplo de Trabajo
from pathlib import Path
def load_word_counts(paths: list[Path]) -> dict[str, int]:
words = (
word.lower()
for path in paths
for line in path.read_text(encoding="utf-8").splitlines()
for word in line.split()
)
return {word: len(word) for word in sorted(set(words))}
def active_user_emails(rows: list[dict[str, object]]) -> list[str]:
return [
str(row["email"])
for row in rows
if row.get("active") and "email" in row
]
def chunk_indices(size: int, chunk: int) -> list[tuple[int, int]]:
return [(i, min(i + chunk, size)) for i in range(0, size, chunk)]
if __name__ == "__main__":
print(active_user_emails([
{"email": "a@x.com", "active": True},
{"email": "b@x.com", "active": False},
]))
print(chunk_indices(10, 3))Lo que esto demuestra:
- La expresión generadora anidada aplana archivos sin listas intermedias
- La comprensión de listas filtra y proyecta filas de diccionarios
- La comprensión de conjuntos dentro de la comprensión de diccionarios elimina duplicados de palabras antes de mapear la longitud
- El argumento generador para
sumevita asignar 100k enteros
Análisis Profundo
Cómo Funciona
- Comprensiones Eager (ávidas) - Las comprensiones de listas/diccionarios/conjuntos construyen la colección completa inmediatamente.
- Expresiones Generadoras - Las expresiones entre paréntesis
(x for x in it)devuelven un objeto generador. - Expresión Única - No hay sentencias dentro de las comprensiones, solo expresiones y filtros
if. - Ámbito (Scope) - Las comprensiones tienen su propio ámbito local en Python 3 (las variables del bucle no se filtran).
- Encadenamiento - Múltiples cláusulas
forse anidan de izquierda a derecha como bucles anidados.
Cuándo Elegir Cuál
| Forma | Memoria | Reutilización |
|---|---|---|
Comprensión de lista [...] | Almacena todos los elementos | Sí - acceso aleatorio |
Comprensión de conjunto {...} | Almacena elementos únicos | Sí |
Comprensión de diccionario {k: v ...} | Almacena todos los pares | Sí |
Generador (...) | Estado del iterador O(1) | Consumir una vez |
Notas de Python
# Walrus (operador de asignación) en comprensión (3.8+)
filtered = [y for x in data if (y := transform(x)) is not None]
# dict.fromkeys para inicialización simple de claves
unique_order = list(dict.fromkeys(items)) # preservar orden, eliminar duplicadosTrampas Comunes
- Efectos secundarios en comprensiones -
[print(x) for x in items]construye una lista deNone. Solución: Use un bucleforsimple para efectos. - Comprensión de lista enorme -
[process(x) for x in billion_rows]agota la RAM. Solución: Generador y procesamiento en streaming. - Anidamiento ilegible - Tres cláusulas
foren una sola línea oscurecen la intención. Solución: Dividir en funciones auxiliares o usaritertools. - Colisión de claves en comprensión de diccionario - Las claves posteriores sobrescriben las anteriores silenciosamente. Solución: Validar la unicidad o usar un bucle con comprobaciones.
- Generador agotado - Iterar dos veces no produce nada la segunda vez. Solución: Materializar a lista o recrear el generador.
Alternativas
| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
| Expresión generadora | Flujos grandes o infinitos | Necesita indexación o len() |
Bucle for | Efectos secundarios, ramificación compleja | map/filter simple |
map/filter | Estilo funcional con funciones | Múltiples cláusulas if/for |
itertools | Ventanas deslizantes, encadenamiento | Comprensión simple de una línea |
Preguntas Frecuentes
¿Puedo usar `else` en una comprensión?
Sí: [x if x > 0 else 0 for x in nums] - la expresión antes de for, no es lo mismo que el else del bucle.
¿Son las comprensiones más rápidas que los bucles?
A menudo sí en CPython: bytecode optimizado y sin búsqueda repetida de métodos para append.
¿Cuándo debo usar un generador?
Cuando los datos no caben en memoria o solo se pasan una vez para funciones de agregación.
¿Las variables de comprensión se filtran?
No en Python 3 - x en [x for x in items] es local a la comprensión.
¿Cómo aplano una lista anidada?
[y for row in matrix for y in row] - múltiples cláusulas for aplanan un nivel.
¿Puedo construir un diccionario a partir de dos listas?
{k: v for k, v in zip(keys, values)} - o dict(zip(keys, values)) cuando no se necesita filtro.
¿Qué es una comprensión de conjuntos?
{expr for x in items} - llaves sin dos puntos. Produce un conjunto, no un diccionario.
¿Debo asignar un generador a una variable?
Sí, al reutilizar la definición de la canalización: gen = (x for x in data if pred(x)) luego páselo a funciones.
¿Cómo depuro una comprensión compleja?
Expándala temporalmente a un bucle for, o extraiga la expresión interna a una función con nombre.
¿Está `list(map(...))` obsoleto?
No siempre - map con una función existente puede ser más claro. Las comprensiones ganan para transformaciones en línea con filtros.
Relacionado
- Generadores y yield - funciones generadoras completas
- itertools - composición avanzada de iteradores
- Listas y Tuplas - operaciones de listas
- Flujo de Control - cuándo los bucles superan a las comprensiones
Versiones de la Pila: Esta página fue escrita para Python 3.14.0 (estable 3.14, mantenimiento 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, y uv 0.6+.