Los iteradores y generadores son la base del procesamiento eficiente de secuencias en Python. Históricamente, Python ha tratado de simplificar el trabajo con flujos de datos y evitar el almacenamiento excesivo de grandes colecciones en memoria. Primero se introdujo el soporte para los iteradores a través de los protocolos __iter__ y __next__, y luego los generadores, que permiten crear iteradores simples utilizando construcciones basadas en yield.
Problema: a menudo es necesario procesar grandes volúmenes de datos (por ejemplo, streaming desde un archivo o una base de datos), lo que no se puede hacer de manera conveniente y eficiente si se carga todo en memoria a la vez. Las funciones normales devuelven todos los resultados de inmediato, y crear sus propios iteradores a través de clases a menudo es demasiado engorroso para casos simples.
Solución: el mecanismo yield permite organizar la generación de datos "perezosamente". La función generadora no devuelve una lista u otra colección, sino que devuelve un objeto generador: un iterador que calcula valores según sea necesario.
Ejemplo de código:
# Generador simple def countdown(n): while n > 0: yield n n -= 1 for i in countdown(3): print(i) # 3, 2, 1
Características clave:
¿Se puede usar return y yield en una misma función?
Sí, pero return en un generador finaliza la iteración (lanza StopIteration), mientras que yield se puede usar tantas veces como sea necesario.
def example(): yield 1 return # StopIteration
¿Por qué no se puede "reiniciar" un generador después de que ha terminado?
Un generador después de finalizar las iteraciones (StopIteration) no puede ser reiniciado, debe ser creado de nuevo.
gen = countdown(2) list(gen) # [2, 1] list(gen) # [] (el generador ya se ha agotado)
¿Cuál es la diferencia entre un generador y un iterador?
Un generador es un caso particular de un iterador; cualquier objeto con los métodos iter y next es un iterador, pero un generador se crea a través de una función con yield.
Un desarrollador escribió una función que carga un millón de líneas de un archivo en memoria a través de list(fd) para su análisis. Esto resultó en un desbordamiento de memoria en el servidor.
Pros:
Contras:
Uso de un generador para leer un archivo línea por línea y analizar datos sobre la marcha utilizando yield.
Pros:
Contras: