ProgramaciónIngeniero de Datos

Explique cómo funciona la evaluación perezosa en Python y dónde se aplica además de en los generadores. ¿Cuáles son las ventajas de los cálculos perezosos?

Supere entrevistas con el asistente de IA Hintsage

Respuesta.

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:

  • No todos los elementos se almacenan en memoria: se calculan "bajo demanda".
  • Puede trabajar con flujos de datos infinitos.
  • Permite procesar colecciones que no existen físicamente en su totalidad (por ejemplo, un flujo de datos de una red).

Preguntas trampa.

¿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.

Errores típicos y anti-patrones

  • Convierte implícitamente objetos perezosos en listas, perdiendo la ventaja del ahorro de memoria.
  • Considera iteradores y generadores como intercambiables con listas (pero no se pueden indexar ni recorrer nuevamente).
  • Aplica len() a objetos perezosos y se encuentra con errores.

Ejemplo de la vida real

Caso negativo

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:

  • Indexación rápida por línea específica.

Desventajas:

  • Alto consumo de memoria.
  • El programa no escala a grandes datos.

Caso positivo

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:

  • Trabaja con archivos de cualquier tamaño.
  • Mínimo uso de memoria.

Desventajas:

  • Si se necesita acceso aleatorio por índice, se requerirá una estructura de datos separada.