2.30. Comprensiones

Una comprensión construye una nueva lista, conjunto, diccionario o generador a partir de un iterable existente, en una sola expresión. Es un reemplazo del patrón habitual de empezar con un contenedor vacío e ir añadiendo elementos en un bucle.

2.30.1. Comprensiones de listas

squares = [x * x for x in range(5)]
print(squares)

Salida:

[0, 1, 4, 9, 16]

El mismo bucle escrito de forma explícita:

squares = []
for x in range(5):
    squares.append(x * x)

La forma de comprensión es una sola expresión que construye la lista en el sitio. No hay squares = [] ni .append: el resultado es el valor de la comprensión.

La expresión "[f(x) for x in xs if cond]" anotada: la expresión inicial es el resultado, la cláusula for nombra la variable del bucle y la cláusula if filtra qué elementos se conservan.

La expresión inicial produce cada elemento; la cláusula for nombra la variable del bucle; una cláusula if opcional filtra elementos.

2.30.2. Filtrar con if

Una cláusula if opcional conserva solo los elementos que coinciden:

evens = [x for x in range(10) if x % 2 == 0]
print(evens)

Salida:

[0, 2, 4, 6, 8]

El filtro se ejecuta antes que la expresión inicial: x % 2 == 0 se comprueba primero; solo los valores coincidentes llegan a x para la salida.

2.30.3. Comprensiones de diccionarios y conjuntos

La misma forma funciona con literales de diccionario y de conjunto.

Una comprensión de diccionario tiene un par key: value antes del for:

squares = {x: x * x for x in range(5)}
print(squares)

Salida:

{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

Una comprensión de conjunto usa llaves y una sola expresión:

unique_lengths = {len(w) for w in ["a", "bb", "c", "bb"]}
print(unique_lengths)

Salida:

{1, 2}

2.30.4. Expresiones generadoras

Los paréntesis redondos producen una expresión generadora en lugar de una lista. Los valores se calculan de uno en uno, bajo demanda:

total = sum(x * x for x in range(1000))

Nunca se construye una lista de un millón de elementos. Los valores fluyen uno a uno hacia sum(), que los suma y descarta cada uno a medida que avanza.

Las expresiones generadoras son la opción correcta cuando se alimentan valores a una función reductora (sum(), max(), any(), all()) o a cualquier otro código que consuma un iterador: ahorran la memoria que habría usado la lista equivalente.

2.30.5. Cuándo no usar una comprensión

Las comprensiones son concisas, pero no siempre más claras. Recurre a un bucle for normal cuando:

  • El cuerpo necesita más de una sentencia (una comprensión admite exactamente una expresión).

  • El cuerpo tiene efectos secundarios (imprimir, escribir en un archivo): las comprensiones sirven para construir una colección, no para ejecutar acciones.

  • El filtro o la transformación tiene tantas partes que la comprensión ya no se lee de izquierda a derecha.