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.