Introducción

Los generadores en Python son una forma especial de iteradores que permiten crear secuencias de valores de manera eficiente y perezosa (lazy evaluation). A diferencia de las funciones normales que devuelven un valor y terminan su ejecución, los generadores pueden pausar su ejecución y reanudarla más tarde, lo que los hace ideales para trabajar con grandes conjuntos de datos o flujos de datos continuos.

Conceptos Clave

  • Iterador: Un objeto que implementa los métodos __iter__() y __next__(), permitiendo iterar sobre sus elementos uno a uno.
  • Generador: Un tipo especial de iterador que se define utilizando una función con la palabra clave yield en lugar de return.
  • Lazy Evaluation: Evaluación diferida de una expresión hasta que su valor sea necesario, lo que permite ahorrar memoria y mejorar el rendimiento.

Creación de Generadores

Usando Funciones con yield

La forma más común de crear un generador es mediante una función que utiliza la palabra clave yield para devolver valores uno a uno.

def contador(maximo):
    contador = 0
    while contador < maximo:
        yield contador
        contador += 1

# Uso del generador
for numero in contador(5):
    print(numero)

Explicación:

  1. La función contador es un generador porque utiliza yield.
  2. Cada vez que se llama a yield, la ejecución de la función se pausa y se devuelve el valor actual de contador.
  3. La próxima vez que se itera sobre el generador, la ejecución se reanuda justo después de la última llamada a yield.

Usando Expresiones Generadoras

Las expresiones generadoras son similares a las comprensiones de listas, pero en lugar de crear una lista en memoria, crean un generador.

# Expresión generadora
generador = (x * x for x in range(5))

# Uso del generador
for valor in generador:
    print(valor)

Explicación:

  1. La expresión (x * x for x in range(5)) crea un generador que calcula los cuadrados de los números del 0 al 4.
  2. Al iterar sobre el generador, se calculan y devuelven los valores uno a uno.

Ejemplos Prácticos

Generador de Números Fibonacci

def fibonacci(n):
    a, b = 0, 1
    for _ in range(n):
        yield a
        a, b = b, a + b

# Uso del generador
for numero in fibonacci(10):
    print(numero)

Explicación:

  1. La función fibonacci genera los primeros n números de la secuencia de Fibonacci.
  2. Cada llamada a yield devuelve el valor actual de a y luego actualiza a y b.

Generador Infinito

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

# Uso del generador
for numero in contador_infinito():
    if numero > 10:
        break
    print(numero)

Explicación:

  1. La función contador_infinito genera un contador infinito.
  2. El bucle while True asegura que el generador nunca se detenga a menos que se rompa explícitamente.

Ejercicios Prácticos

Ejercicio 1: Generador de Números Pares

Instrucciones: Crea un generador que devuelva los primeros n números pares.

def numeros_pares(n):
    for i in range(n):
        yield i * 2

# Uso del generador
for numero in numeros_pares(5):
    print(numero)

Solución:

def numeros_pares(n):
    for i in range(n):
        yield i * 2

# Uso del generador
for numero in numeros_pares(5):
    print(numero)

Ejercicio 2: Generador de Números Primos

Instrucciones: Crea un generador que devuelva los primeros n números primos.

def es_primo(num):
    if num < 2:
        return False
    for i in range(2, int(num ** 0.5) + 1):
        if num % i == 0:
            return False
    return True

def numeros_primos(n):
    contador, num = 0, 2
    while contador < n:
        if es_primo(num):
            yield num
            contador += 1
        num += 1

# Uso del generador
for primo in numeros_primos(5):
    print(primo)

Solución:

def es_primo(num):
    if num < 2:
        return False
    for i in range(2, int(num ** 0.5) + 1):
        if num % i == 0:
            return False
    return True

def numeros_primos(n):
    contador, num = 0, 2
    while contador < n:
        if es_primo(num):
            yield num
            contador += 1
        num += 1

# Uso del generador
for primo in numeros_primos(5):
    print(primo)

Conclusión

Los generadores son una herramienta poderosa en Python para manejar secuencias de datos de manera eficiente y con un uso mínimo de memoria. Al utilizar yield, puedes crear iteradores personalizados que pueden pausar y reanudar su ejecución, lo que es especialmente útil para trabajar con grandes conjuntos de datos o flujos de datos continuos. Con la práctica, los generadores pueden convertirse en una parte esencial de tu caja de herramientas de programación en Python.

Curso de Programación en Python

Módulo 1: Introducción a Python

Módulo 2: Estructuras de Control

Módulo 3: Funciones y Módulos

Módulo 4: Estructuras de Datos

Módulo 5: Programación Orientada a Objetos

Módulo 6: Manejo de Archivos

Módulo 7: Manejo de Errores y Excepciones

Módulo 8: Temas Avanzados

Módulo 9: Pruebas y Depuración

Módulo 10: Desarrollo Web con Python

Módulo 11: Ciencia de Datos con Python

Módulo 12: Proyecto Final

© Copyright 2024. Todos los derechos reservados