yield: crea generadores eficientes en Python

Actualizado el: 20/08/2026
Tempo de leitura: 5 minutos
Criação de geradores eficientes usando yield em Python

La palabra clave yield transforma una función normal en un generador. En lugar de calcular y almacenar todos los resultados antes de devolverlos, el generador produce un valor, pausa su ejecución y continúa desde el mismo punto cuando se solicita el siguiente elemento. Esta evaluación perezosa permite procesar secuencias grandes con un consumo de memoria mucho menor.

Para comprender bien este tema, conviene conocer los bucles for en Python y las comprensiones de listas. Un generador puede reemplazar una lista cuando no necesitas conservar todos los valores simultáneamente.

Una función normal frente a un generador

Una función con return finaliza y devuelve un resultado. Una función con yield entrega un valor temporalmente y conserva su estado.

def numeros():
    yield 1
    yield 2
    yield 3

generador = numeros()
print(next(generador))
print(next(generador))
print(next(generador))

Cada llamada a next() reanuda la función hasta encontrar el siguiente yield. Cuando no quedan valores, se genera StopIteration. Los bucles for gestionan esa excepción automáticamente.

Por qué yield ahorra memoria

Una lista crea todos sus elementos de inmediato. Un generador produce cada elemento cuando se necesita.

def cuadrados(limite):
    for numero in range(limite):
        yield numero * numero

for cuadrado in cuadrados(1_000_000):
    if cuadrado > 100:
        break
    print(cuadrado)

El generador no calcula el millón de cuadrados. El bucle se detiene al superar 100 y el resto nunca se produce. Esta característica es especialmente útil para archivos, flujos de red, consultas paginadas y pipelines.

Leer archivos grandes línea por línea

from pathlib import Path

def lineas_no_vacias(ruta):
    with Path(ruta).open(encoding="utf-8") as archivo:
        for linea in archivo:
            texto = linea.strip()
            if texto:
                yield texto

for linea in lineas_no_vacias("datos.txt"):
    procesar(linea)

Este patrón mantiene una sola línea principal en memoria. La guía para leer archivos gigantes sin bloquear Python presenta otras técnicas como procesamiento por lotes y lectura en chunks.

Generadores infinitos

Un generador puede representar una secuencia sin final siempre que el consumidor decida cuándo detenerse.

def contador(inicio=0):
    actual = inicio
    while True:
        yield actual
        actual += 1

for numero in contador(10):
    print(numero)
    if numero == 15:
        break

No conviertas un generador infinito a lista. La operación nunca terminaría y acabaría consumiendo la memoria disponible.

Expresiones generadoras

Las expresiones generadoras utilizan una sintaxis parecida a una comprensión, pero con paréntesis.

cuadrados = (numero * numero for numero in range(10))
print(sum(cuadrados))

Una lista usa corchetes y conserva todos los valores; una expresión generadora usa paréntesis y los produce bajo demanda. Para conjuntos pequeños que serán reutilizados, una lista puede ser más práctica. Para secuencias grandes o una sola pasada, el generador suele ser mejor.

Encadenar transformaciones

Los generadores permiten crear pipelines donde cada etapa recibe y produce elementos.

def leer_numeros(ruta):
    with open(ruta, encoding="utf-8") as archivo:
        for linea in archivo:
            yield int(linea.strip())

def solo_pares(numeros):
    for numero in numeros:
        if numero % 2 == 0:
            yield numero

def duplicar(numeros):
    for numero in numeros:
        yield numero * 2

resultado = duplicar(solo_pares(leer_numeros("numeros.txt")))
for valor in resultado:
    print(valor)

Cada etapa procesa un elemento y lo entrega a la siguiente. La memoria se mantiene estable incluso cuando el archivo es grande.

Usar yield from

yield from delega la producción de valores a otro iterable o generador.

def recorrer_grupos(grupos):
    for grupo in grupos:
        yield from grupo

print(list(recorrer_grupos([[1, 2], [3, 4], [5]])))

Esta sintaxis evita escribir un segundo bucle manual. También transmite operaciones avanzadas como send() y excepciones entre generadores, aunque esas capacidades rara vez son necesarias en código inicial.

Enviar valores con send()

Un generador puede recibir información mientras está pausado.

def acumulador():
    total = 0
    while True:
        valor = yield total
        if valor is None:
            return
        total += valor

g = acumulador()
print(next(g))
print(g.send(10))
print(g.send(5))

Este mecanismo es potente, pero puede hacer que el flujo sea difícil de seguir. Para la mayoría de los proyectos, generadores simples que solo producen valores son más mantenibles.

Generadores y excepciones

Los recursos abiertos dentro de un generador deben cerrarse correctamente. Un bloque with es la opción más segura. Si el consumidor abandona antes de terminar, Python puede cerrar el generador, pero no conviene depender de detalles implícitos para recursos críticos.

def registros(ruta):
    with open(ruta, encoding="utf-8") as archivo:
        for linea in archivo:
            try:
                yield convertir(linea)
            except ValueError as error:
                registrar_error(error, linea)

Decide si una línea inválida debe omitirse, detener el proceso o enviarse a otra salida. No captures excepciones generales sin registrar el problema.

Combinar generadores con itertools

El módulo itertools ofrece utilidades optimizadas como islice, chain, takewhile y groupby.

from itertools import islice

primeros_cinco = islice(contador(100), 5)
print(list(primeros_cinco))

islice limita un iterable sin crear previamente toda la secuencia.

Medir rendimiento correctamente

Un generador no siempre es más rápido. Su principal ventaja suele ser la memoria y la capacidad de comenzar a producir resultados antes. Si necesitas comparar implementaciones, utiliza timeit y mide también el consumo de memoria. Recuerda consumir completamente el generador durante el benchmark; medir únicamente su creación sería engañoso.

Cuándo no usar yield

Una lista es más adecuada cuando necesitas acceder repetidamente por índice, conocer la longitud inmediatamente, ordenar varias veces o recorrer los valores en distintas ocasiones. Los generadores son de una sola pasada. Una vez consumidos, debes crear otro generador.

Tampoco uses yield solo para hacer el código más sofisticado. Si el conjunto tiene diez elementos y la lista expresa mejor la intención, la claridad es más importante que una optimización irrelevante.

Errores frecuentes

Los errores habituales son intentar reutilizar un generador agotado, aplicar len() directamente, convertir secuencias infinitas a lista y olvidar que el código interno no se ejecuta hasta comenzar la iteración. También es común ocultar errores porque una función generadora no ejecuta su cuerpo al ser llamada, sino al solicitar el primer elemento.

Fuentes oficiales

La referencia oficial de expresiones yield describe el comportamiento de generadores, yield from, send() y cierre. La guía funcional oficial de Python explica la evaluación perezosa y su relación con iteradores.

Conclusión

yield permite construir funciones que producen valores progresivamente y conservan su estado entre iteraciones. Es ideal para archivos grandes, secuencias infinitas y pipelines. Utilízalo cuando una sola pasada y el bajo consumo de memoria aporten valor real, y conserva las listas para colecciones pequeñas que deben reutilizarse o indexarse.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Código Python en pantalla representando EnumDict y enumeraciones avanzadas
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    EnumDict: personaliza Enums con validación

    Aprende EnumDict en Python para personalizar EnumType, validar miembros y crear enumeraciones avanzadas con seguridad.

    Ler mais

    Tempo de leitura: 4 minutos
    11/10/2026
    Rack de servidores que representa el balanceo de conexiones con SO_REUSEPORT_LB en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    SO_REUSEPORT_LB: reparte conexiones entre workers

    Aprende SO_REUSEPORT_LB en Python para distribuir conexiones entre workers con pruebas, portabilidad y cierre ordenado.

    Ler mais

    Tempo de leitura: 5 minutos
    11/10/2026
    Código Python asíncrono en un portátil para inspect.markcoroutinefunction
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    markcoroutinefunction: detecta wrappers async

    Aprende inspect.markcoroutinefunction en Python para identificar wrappers asíncronos, integrar frameworks y evitar detecciones incorrectas.

    Ler mais

    Tempo de leitura: 5 minutos
    10/10/2026
    Código Python para recorrer carpetas y archivos con Path.walk
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    Path.walk: recorre directorios con seguridad

    Aprende Path.walk en Python para recorrer directorios, filtrar archivos, tratar errores y controlar la travesía con seguridad.

    Ler mais

    Tempo de leitura: 5 minutos
    10/10/2026
    Depuración de un proceso Python en terminal con código
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    pdb -p: depura procesos Python en ejecución

    Aprende a conectar pdb a un proceso Python en ejecución, inspeccionar la pila y diagnosticar bloqueos de forma segura.

    Ler mais

    Tempo de leitura: 6 minutos
    09/10/2026
    Código Python para representar fracciones exactas
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    fractions.from_number: convierte números en fracciones

    Aprende fractions.from_number en Python para convertir números en fracciones exactas, controlar precisión, validar entradas y evitar redondeos inesperados.

    Ler mais

    Tempo de leitura: 4 minutos
    09/10/2026