Decoradores, Iteradores y Generadores en Python

Decoradores sin argumentos

Un decorador es una función que toma otra función y extiende su comportamiento sin modificar su código. El siguiente ejemplo mide el tiempo de ejecución:

import time

def medir_tiempo(funcion):
    def envoltura(*args, **kwargs):
        inicio = time.time()
        resultado = funcion(*args, **kwargs)
        fin = time.time()
        print(f"Tiempo de ejecución: {fin - inicio:.4f} segundos")
        return resultado
    return envoltura

@medir_tiempo
def saludo():
    print("¡Hola mundo!")
    time.sleep(1)
    return "Saludo completado"

print(saludo())

Cuando se ejecuta @medir_tiempo, Python llama a medir_tiempo(saludo) y reemplaza saludo con la función envoltura.

Decoradores con argumentos

Para pasar argumentos al decorador se necesita una capa adicional:

def repetir(veces):
    def decorador(funcion):
        def envoltura(*args, **kwargs):
            for _ in range(veces):
                resultado = funcion(*args, **kwargs)
            return resultado
        return envoltura
    return decorador

@repetir(3)
def mostrar_mensaje():
    print("Ejecutando función")

Iteradores

Un objeto iterable es cualquier objeto que implemente el método __iter__(). Al llamarlo, se obtiene un iterador, el cual posee el método __next__() para acceder a sus elementos uno a uno.

Los iteradores también implementan __iter__(), devolviéndose a sí mismos. Esto permite que tanto iterables como iteradores funcionen bajo el mismo protocolo en bucles for.

Funcionamiento del bucle for

# Internamente, un bucle for hace:
# 1. Obtiene un iterador mediante __iter__()
# 2. Llama repetidamente __next__() hasta capturar StopIteration

diccionario = {"a": 1, "b": 2, "c": 3}
it = iter(diccionario)  # equivalente a diccionario.__iter__()

while True:
    try:
        clave = next(it)   # equivalente a it.__next__()
        print(f"{clave}: {diccionario[clave]}")
    except StopIteration:
        break

Generadores

Un generador es una función que produce una secuencia de valores usando la palabra clave yield. Cada llamada a next() reanuda la ejecución hasta el siguiente yield.

def contador_hasta(n):
    i = 1
    while i <= n:
        print(f"Generando {i}")
        yield i
        i += 1
    print("Fin del generador")

gen = contador_hasta(3)

print(next(gen))  # 1
print(next(gen))  # 2
print(next(gen))  # 3
print(next(gen))  # StopIteration

Los generadores son una forma elegante de crear iteradores sin necesidad de definir una clase completa.

Etiquetas: Python decoradores iteradores generadores Yield

Publicado el 8-11 04:37