Lecciones del módulo (3/4)
Generator expressions
Una expresión generadora (generator expression) es similar a una comprensión de lista, pero utiliza paréntesis en lugar de corchetes. La diferencia es drástica: no construye una lista en memoria, sino que produce los elementos bajo demanda, uno a la vez.
gen = (n * n for n in range(1_000_000))
# instantáneo, memoria constante: aún no se ha hecho ningún cálculo
list_comp = [n * n for n in range(1_000_000)]
# asigna 1 millón de enteros en memoriaEvaluación perezosa (lazy evaluation)
El generador no hace nada hasta que alguien le pide el siguiente valor:
gen = (n * n for n in range(5))
next(gen) # 0 (calculado ahora)
next(gen) # 1
next(gen) # 4
# ... y así sucesivamente, hasta StopIterationEl generador se agota: después de consumir todos los elementos, queda vacío para siempre. Para volver a usarlo, debes recrearlo.
Cuándo usarlo
Cuando pasas los resultados a una función que los consume en secuencia:
total = sum(n * n for n in range(1_000_000))
# sin lista intermedia, memoria constantesum, any, all, max, min, "".join(...) aceptan generadores.
Si la función externa solo requiere los paréntesis de llamada, puedes
omitir los propios del generador:
sum((n * n for n in range(10))) # OK
sum(n * n for n in range(10)) # IDÉNTICO, más legibleany y all: cortocircuito
any(...) y all(...) detienen la iteración tan pronto como tienen la respuesta.
Con un generador, los elementos siguientes ni siquiera se calculan.
nums = [1, 2, 3, 4, 5]
any(n > 3 for n in nums) # True (se detiene en 4)
all(n > 0 for n in nums) # True
all(n > 2 for n in nums) # False (se detiene en 1)Patrón: tubería perezosa (lazy pipeline)
righe = (linea.strip() for linea in testo.splitlines())
non_vuote = (r for r in righe if r)
prime_dieci = []
for r in non_vuote:
prime_dieci.append(r)
if len(prime_dieci) == 10:
breakSin listas intermedias, incluso con entradas gigantescas.
Consumir generadores con sum, any, all
Las expresiones generadoras son perfectas para pasarse directamente a funciones que consumen iterables, como sum(), any() o all(). En este caso, puedes omitir los paréntesis externos de la expresión generadora, simplemente escribiendo:
squares_sum = sum(x**2 for x in range(10))Esto mantiene tu código limpio y sumamente eficiente en memoria.
Pruébalo tú
Calcula la suma de los cuadrados de los números del 1 al 100 (inclusive) usando una expresión generadora dentro de sum(). Asígnala a `tot`. Evalúa `tot`.
Mostrar pista
range(1, 101) incluye 100.
Solución disponible después de 3 intentos
Ejercicio de repaso
Dada la lista `words = ['ciao', 'mondo', 'PYTHON', 'java']`, usa any() con una expresión generadora para comprobar si AL MENOS una palabra está en mayúsculas (.isupper()). Asigna el resultado a `has_uppercase`. Evalúa `has_uppercase`.
Mostrar pista
any(p.isupper() for p in words)
Solución disponible después de 3 intentos
Desafío adicional
Usa una expresión generadora pasada directamente a la función `sum()` para calcular la suma de los cuadrados de todos los enteros desde 1 hasta 100 inclusive. Guarda el resultado en `total_sum` y evalúalo.
Mostrar pista
Escribe total_sum = sum(x**2 for x in range(1, 101)). No se necesitan dobles paréntesis.
Solución disponible después de 3 intentos