Conceptos fundamentales en Python para el manejo de flujos y funciones
En Python, existen mecanismos clave para el control de datos y la reutilización de código, incluyendo objetos iterables, iteradores, generadores, clausuras y decoradores.
1. Objetos iterables
Un objeto iterable es aquel que implementa el método __iter__(), permitiendo su uso en bucles for. Por ejemplo, las listas y tuplas son iterables por defecto.
from collections.abc import Iterable
ejemplo_lista = [10, 20, 30]
print(isinstance(ejemplo_lista, Iterable)) # Salida: True
1.1 Iteradores
Un iteardor es un objeto que implementa tanto __iter__() como __next__(), proporcionando acceso secuencial a sus elementos. A continuación, se muestra un ejemplo de un iterador personalizado que reecorre una lista en orden inverso.
class MiIterador:
def __init__(self, datos):
self.datos = datos
self.indice = len(datos) - 1
def __iter__(self):
return self
def __next__(self):
if self.indice >= 0:
valor = self.datos[self.indice]
self.indice -= 1
return valor
else:
raise StopIteration
iterador_instancia = MiIterador([5, 10, 15, 20])
for item in iterador_instancia:
print(f'Dato obtenido: {item}')
1.2 Bucle for
El bucle for en Python internamente llama a iter() para obtener un iterador del objeto iterable y luego utiliza next() para extraer elemantos, capturando StopIteration para finalizar la iteración.
2. Generadores
Los generadores son una forma especial de iteradores que producen valores de manera lazy, ahorrando memoria. Se pueden crear mediante expresiones generadoras o funciones con yield.
2.1 Expresión generadora
generador_expresion = (x**2 for x in range(4))
print(type(generador_expresion)) # Salida: <class 'generator'>
2.2 Función generadora
def fibonacci_gen():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
gen_func = fibonacci_gen()
print(type(gen_func)) # Salida: <class 'generator'>
print(next(gen_func), next(gen_func), next(gen_func)) # Salida: 0 1 1
3. Clausuras
Una clausura se define cuando una función anidada referencia variables de su función contenedora, manteniendo su estado incluso después de que la función externa termine.
def crear_contador(inicio):
valor = inicio
def incrementar():
nonlocal valor
valor += 1
return valor
return incrementar
contador = crear_contador(10)
print(contador()) # Salida: 11
print(contador()) # Salida: 12
4. Decoradores
Los decoradores permiten modificar o extender el comportamiento de funciones o clases sin cambiar su código fuente. Se implementan comúnmente usando clausuras o clases.
4.1 Decorador con función
def mi_decorador(func):
def envoltura():
print("Antes de la ejecución")
func()
print("Después de la ejecución")
return envoltura
@mi_decorador
def saludar():
print("Hola, mundo")
saludar()
4.2 Decorador con clase
class DecoradorClase:
def __init__(self, func):
self.func = func
def __call__(self):
print("Decorador de clase activado")
self.func()
@DecoradorClase
def tarea():
print("Ejecutando tarea")
tarea()
4.3 Múltiples decoradores
Cuando se aplican varios decoradores, se evalúan de abajo hacia arriba al definir la función, y luego de arriba hacia abajo al llamarla.
def decorador_a(func):
print("Cargando decorador A")
def envoltura_a():
print("Ejecutando envoltura A")
func()
return envoltura_a
def decorador_b(func):
print("Cargando decorador B")
def envoltura_b():
print("Ejecutando envoltura B")
func()
return envoltura_b
@decorador_a
@decorador_b
def operacion():
print("Función principal")
operacion()
# Salida al llamar:
# Ejecutando envoltura A
# Ejecutando envoltura B
# Función principal
Los decoradores facilitan la aplicación de funcionalidades transversales, como logging o validación, de manera modular.