yield en Python: crea generadores eficientes

Publicado el: 11/07/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

    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    Decoradores en Python: guía práctica y ejemplos

    Aprende decoradores en Python con wrappers, argumentos, functools.wraps, parámetros, validación, rendimiento y buenas prácticas.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Caixas empilhadas
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    Módulos y paquetes en Python: guía completa

    Aprende módulos y paquetes en Python: imports, __init__.py, estructura de proyectos, ejecución con -m, imports relativos y errores circulares.

    Ler mais

    Tempo de leitura: 5 minutos
    11/07/2026
    logo do python com objetos abaixo do logo
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    Programación orientada a objetos en Python

    Aprende programación orientada a objetos en Python con clases, objetos, atributos, métodos, propiedades, herencia, composición y ejemplos prácticos.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Programação assíncrona com asyncio em Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    asyncio en Python: guía práctica para principiantes

    Aprende asyncio en Python con coroutines, await, tasks, TaskGroup, timeouts, cancelación, colas, bloqueos y manejo de errores.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Uso de expressões regulares regex para manipulação de texto em Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    Regex en Python: guía de expresiones regulares

    Aprende regex en Python con search, fullmatch, findall, grupos, cuantificadores, sustituciones, flags, patrones compilados y ejemplos prácticos.

    Ler mais

    Tempo de leitura: 5 minutos
    11/07/2026