Módulo INTERMEDIOLección 04 de 10

Iteradores y generadores

Produce valores uno a la vez, perezosamente: memoria eficiente.

01

Teoría y Fundamentos

Lee y asimila los conceptos clave antes de evaluar

Imagina procesar un archivo de 10 millones de líneas. Cargarlo todo en una lista consumiría gigas de memoria. Un generador produce cada valor justo cuando se necesita: memoria constante, trabajo diferido.

Iteradores: el protocolo detrás del for

Cuando escribes for x in lista, Python le pide a la lista un iterador y le va pidiendo valores con next() hasta que recibe la señal de fin (StopIteration):

numeros = iter([1, 2, 3])   # obtener un iterador manualmente

print(next(numeros))   # 1
print(next(numeros))   # 2
print(next(numeros))   # 3
next(numeros)          # StopIteration → agotado

Un iterador es de un solo uso: agotado, no se reinicia.

Generadores: funciones con yield

Un generador es una función que en vez de return usa yield: produce valores uno a la vez y congela su estado entre llamada y llamada:

def contar_hasta(n):
    i = 1
    while i <= n:
        yield i        # entrega el valor y PAUSA aquí
        i += 1         # reanuda aquí en el próximo next()

gen = contar_hasta(3)
print(next(gen))   # 1
print(next(gen))   # 2

for numero in contar_hasta(3):   # el for consume el generador
    print(numero)

La diferencia clave con una lista: los valores no existen todos a la vez. Se fabrican bajo demanda.

Compara el consumo

def primeros_millones_lista():
    return [x for x in range(10_000_000)]     # ~400 MB en memoria

def primeros_millones_gen():
    for x in range(10_000_000):
        yield x                                # bytes en memoria

range() mismo es perezoso — por eso puedes hacer for i in range(10**9) sin morir.

Expresiones generadoras

Como una list comprehension pero con paréntesis:

cuadrados_lista = [x ** 2 for x in range(5)]   # lista: todo ya calculado
cuadrados_gen = (x ** 2 for x in range(5))     # generador: bajo demanda

print(sum(x ** 2 for x in range(1_000_000)))   # ¡sin crear lista intermedia!

sum(), max(), any(), all() aceptan generadores directamente.

yield en cadena: pipelines

Los generadores se componen como tuberías:

def leer_lineas(texto):
    for linea in texto.split("\n"):
        yield linea

def solo_comentarios(lineas):
    for linea in lineas:
        if linea.strip().startswith("#"):
            yield linea

codigo = "# config\nx = 1\n# fin"
for comentario in solo_comentarios(leer_lineas(codigo)):
    print(comentario)     # # config / # fin

Cada etapa produce de a un elemento: el pipeline nunca carga todo en memoria.

Resumen

  • for consume iteradores con next() hasta StopIteration
  • Un generador (yield) produce valores perezosamente recordando su estado
  • Memoria constante vs listas que cargan todo
  • (x for x in ...) es la expresión generadora — ideal con sum/max/any
  • Generadores de un solo uso; si necesitas recorrer dos veces, crea uno nuevo

¿Terminaste de leer la teoría?

Marca este paso para registrar tu avance y saltar a las preguntas

02

Quiz de Comprobación

3 preguntas de comprensión rápida (requiere 70% para aprobar)

  1. 1

    ¿Qué caracteriza a un generador (una función con yield)?

  2. 2

    ¿Qué ocurre al pedir next() a un generador ya agotado?

  3. 3

    ¿Cuál es la ventaja principal de un generador frente a una lista?

03

Laboratorio de Código Python

2 ejercicios interactivos ejecutados en tu navegador

EJERCICIO 01Python 3.12

Generador de Fibonacci

Completa el generador fib_gen(n) que produzca los primeros n números de Fibonacci con yield. Imprime list(fib_gen(6)).

ejercicio_1.py
Tab: 4 spaces

Iniciando editor de código…

Ctrl/Cmd + Enter para ejecutar
EJERCICIO 02Python 3.12

Pares perezosos

Completa el generador `pares(limite)` que produzca los números pares menores que limite, uno por uno con yield.

ejercicio_2.py
Tab: 4 spaces

Iniciando editor de código…

Ctrl/Cmd + Enter para ejecutar