La evaluación perezosa (lazy evaluation) es un concepto clave de la programación eficiente, donde los valores se calculan solo cuando son necesarios. Históricamente, en Python, todas las estructuras integradas principales (listas, tuplas) eran "codiciosas": crean y almacenan de antemano todos los elementos en la memoria. Con el aumento de volúmenes de datos y tareas de procesamiento de flujos, surgió la necesidad de cálculos perezosos.
Problema: los cálculos codiciosos llevan a un uso ineficiente de la memoria y el tiempo en situaciones donde se pueden obtener resultados gradualmente, por ejemplo, al filtrar, transformar colecciones largas o transmitir archivos.
Solución: en Python, han surgido muchas herramientas para cálculos perezosos: generadores, iteradores, así como funciones de la biblioteca estándar (map, filter, zip, enumerate) y el módulo itertools. Todos ellos devuelven no colecciones listas, sino objetos "perezosos" que devuelven el resultado uno por uno.
Ejemplo de código:
result = map(lambda x: x * x, range(100)) # devolverá un generador-iterador for y in result: print(y) # los valores se calculan a medida que se itera import itertools inf = itertools.count(1) for i in inf: if i > 3: break print(i) # 1, 2, 3
Características clave:
¿Siempre devuelven las funciones map/filter una lista en Python3?
No, en Python 3 estas funciones devuelven iteradores, no listas. Para obtener una lista, es necesario envolver el resultado en list().
x = map(int, ['1', '2']) # <map object> list(x) # [1, 2]
¿Se puede obtener la longitud del resultado de map sin convertirlo en una lista?
No, el iterador no sabe de antemano cuántos elementos tiene hasta que se ha recorrido completamente. Es necesario calcularlo a través de list(), lo que anula la pereza.
¿La función range en Python3 es codiciosa o perezosa?
Perezosa: range crea un "objeto range" — "calcula" los elementos a medida que se solicitan, sin almacenar toda la secuencia.
Un script procesa un enorme archivo CSV, creando una lista de todas las líneas a través de list(open(f)). El servidor "muere" por falta de memoria con un archivo grande.
Ventajas:
Desventajas:
El código utiliza evaluación perezosa: recorre las líneas del archivo con un iterador for line in open(f), o las procesa a través de map/filter sin crear colecciones intermedias.
Ventajas:
Desventajas: