Los generadores son objetos iterables especiales en Python que permiten crear secuencias "sobre la marcha", sin ocupar memoria para toda la colección de una vez. Se implementan mediante funciones con la palabra clave yield o expresiones generadoras ((expr for ... in ...)). Esto es conveniente cuando se trabaja con grandes volúmenes de datos o flujos potencialmente infinitos.
Diferencias clave con las expresiones generadoras:
[x for x in range(10)]) crean toda la lista en memoria de inmediato.(x for x in range(10))) crean elementos uno por uno, consumiendo mucha menos memoria.Cuándo usar generadores:
# Función generadora def contador(n): for i in range(n): yield i for numero in contador(5): print(numero)
"¿Cuál es la diferencia entre usar una función con yield y una función normal que devuelve una lista? Proporcione un ejemplo."
Respuesta:
Una función normal calcula y devuelve la lista de inmediato, ocupando memoria para todos sus elementos. Una función con yield devuelve un generador, que produce elementos uno por uno y no carga toda la secuencia en memoria de inmediato.
def hacer_lista(n): return [i for i in range(n)] # Devuelve la lista de inmediato, ocupa mucha memoria def hacer_generador(n): for i in range(n): yield i # Producirá un elemento a la vez
Historia
En un proyecto para analizar grandes registros se utilizaron expresiones generadoras para extraer líneas que contenían errores:
lineas_error = [linea for linea in open('biglog.txt') if 'ERROR' in linea]
El archivo superaba los 2 GB y la aplicación falló con OOM (Fuera de Memoria). Se necesitaba usar un generador:
lineas_error = (linea for linea in open('biglog.txt') if 'ERROR' in linea)
Historia
Un empleado quería analizar una lista corta, escribió una función con yield, pero olvidó que se devuelve un generador, no una lista:
resultado = mi_funcion_generadora() # resultado es un generador, no una lista if len(resultado) > 5: # TypeError: object of type 'generator' has no len()
Corrección: envolver el resultado en list().
Historia
Se intentó iterar sobre el generador varias veces:
numeros = (i for i in range(5)) for n in numeros: pass # se agotó el generador for n in numeros: print(n) # no imprime nada
El generador es de un solo uso. Se necesita crear uno nuevo para reutilizarlo.