En Python, las funciones se tratan como ciudadanos de primera clase. Esto implica que pueden ser asignadas a variables, transmitidas como argumentos a otras funciones, o retornadas desde funciones.
Funciones de orden superior y alternativas
Las funciones de orden superior, como filter y map, aceptan funciones como argumentos. A menudo, las listas por comprensión ofrecen una sintaxis más legible.
# Uso de map y filter
valores = range(1, 10)
cubos_pares = list(map(lambda x: x ** 3, filter(lambda x: x % 2 == 0, valores)))
# Equivalente con lista por comprensión
cubos_pares_comp = [x ** 3 for x in valores if x % 2 == 0]
Tipos de parámetros
Python soporta parámetros posicionales, argumentos variables (*args), argumentos de palabra clave (**kwargs), y parámetros de palabra clave nombrados. Es esencial documentar los metadatos de los parámetros para mejorar la comprensión del código.
Funciones lambda
Las funciones lambda son anónimas y se utilizan para operaciones simples de una sola expresión.
# Ejemplo de función lambda
duplicar = lambda x: x * 2
salida = duplicar(7) # Resultado: 14
Clausuras y ámbito de variables
Las clausuras permiten que funciones internas retengan el estado de variables de funciones externas. El orden de búsqueda de variables en Python sigue el protocolo LEGB: Local, Encapsulado (Enclosing), Global, y Incorporado (Built-in).
Las palabras clave global y nonlocal se emplean para modificar el alcance de las variables.
Decoradores en Python
Los decoradores son funciones que encapsulan y alteran el comportamiento de otrras funciones. Un caso común es medir el tiempo de ejecución.
from functools import wraps
from time import time
def temporizador(funcion):
"""Decorador para registrar el tiempo de ejecución"""
@wraps(funcion)
def envoltorio(*args, **kwargs):
inicio = time()
resultado = funcion(*args, **kwargs)
duracion = time() - inicio
print(f'La función {funcion.__name__} completada en {duracion:.4f} segundos')
return resultado
return envoltorio
Los decoradores pueden ser parametrizados para mayor flexibilidad, evitando acoplamianto con funciones específicas como print.
from functools import wraps
from time import time
def registrar_mensaje(salida):
"""Decorador con parámetros"""
def decorar(funcion):
@wraps(funcion)
def envoltorio(*args, **kwargs):
inicio = time()
resultado = funcion(*args, **kwargs)
duracion = time() - inicio
salida(funcion.__name__, duracion)
return resultado
return envoltorio
return decorar
Alternativamente, se pueden definir decoradores mediante clases.
from functools import wraps
from time import time
class RegistradorClase:
"""Decorador implementado con una clase"""
def __init__(self, salida):
self.salida = salida
def __call__(self, funcion):
@wraps(funcion)
def envoltorio(*args, **kwargs):
inicio = time()
resultado = funcion(*args, **kwargs)
duracion = time() - inicio
self.salida(funcion.__name__, duracion)
return resultado
return envoltorio
Nota: Al usar @wraps, se puede recuperar la función original sin decorar mediatne func.__wrapped__.
Patrón singleton con decoradores
El patrón singleton garantiza una única instancia de una clase. Un decorador puede implementar este patrón.
from functools import wraps
def singleton(clase):
"""Decorador para aplicar el patrón singleton"""
instancias = {}
@wraps(clase)
def envoltorio(*args, **kwargs):
if clase not in instancias:
instancias[clase] = clase(*args, **kwargs)
return instancias[clase]
return envoltorio
@singleton
class ServicioDatos:
"""Clase singleton para servicio de datos"""
pass
Para escenarios multihilo, se requiere seguridad en la creación de instancias.
from functools import wraps
from threading import RLock
def singleton_hilo_seguro(clase):
"""Decorador singleton seguro para concurrencia"""
instancias = {}
bloqueo = RLock()
@wraps(clase)
def envoltorio(*args, **kwargs):
if clase not in instancias:
with bloqueo:
if clase not in instancias:
instancias[clase] = clase(*args, **kwargs)
return instancias[clase]
return envoltorio
Consejo: La doble verificación dentro del bloqueo mejora el rendimiento, ya que evita adquirir el bloqueo si la instancia ya existe.