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

    Close-up view of a computer screen displaying code in a software development environment.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    ftplib en Python: FTP y FTPS seguros

    Aprende ftplib en Python para listar, descargar y subir archivos por FTP o FTPS con TLS, timeouts, límites, reanudación y

    Ler mais

    Tempo de leitura: 5 minutos
    21/08/2026
    Rack de servidores que representa un endpoint creado con xmlrpc.server en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    xmlrpc.server: crea servidores XML-RPC

    Aprende xmlrpc.server en Python para crear servidores XML-RPC, registrar funciones, limitar métodos y rutas y evitar exposición insegura.

    Ler mais

    Tempo de leitura: 6 minutos
    21/08/2026
    Código de error sobre datos binarios que representa fallos manejados con urllib.error en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    urllib.error en Python: maneja errores HTTP

    Aprende urllib.error en Python para manejar URLError, HTTPError, descargas incompletas, retries selectivos y diagnósticos de red claros.

    Ler mais

    Tempo de leitura: 5 minutos
    21/08/2026
    Teclas con la palabra HTML que representan entidades HTML en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    html.entities: convierte entidades HTML

    Aprende html.entities en Python para consultar entidades HTML, convertir nombres y code points y no confundir decodificación con sanitización.

    Ler mais

    Tempo de leitura: 7 minutos
    21/08/2026
    Carpeta con archivos que representa tipos MIME identificados con mimetypes en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    mimetypes: detecta tipos MIME de archivos

    Aprende mimetypes en Python para identificar tipos de archivo, validar cargas y definir Content-Type con más seguridad.

    Ler mais

    Tempo de leitura: 6 minutos
    20/08/2026
    Código HTML en una pantalla que representa análisis con html.parser en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    html.parser en Python: analiza HTML

    Aprende html.parser en Python para extraer texto, enlaces y metadatos, procesar HTML por bloques y no confundir parsing con sanitización.

    Ler mais

    Tempo de leitura: 4 minutos
    20/08/2026