Funciones en Python: Conceptos Avanzados y Aplicaciones Prácticas

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.

Etiquetas: Python-3 funciones_avanzadas decoradores closures variable_scope

Publicado el 6-28 03:03