Entendiendo los Decoradores en Python

Uno de los aspectos más poderosos pero también más confusos de Python para muchos desarrolladores —especialmente quienes vienen de otros lenguajes— son los decoradores. Este artículo busca ayudarte a entender qué son, cómo funcionan y cómo usarlos correctamente en tus propios proyectos.

¿Qué es un decorador en Python?

Un decorador es, en esencia, una función que recibe otra función como argumento y devuelve una nueva función que puede alterar o extender el comportamiento de la original sin modificar su código fuente. Se basa en dos conceptos fundamentales de Python: funciones de orden superior y closures.

¿Por qué usar decoradores?

  • Separar la lógica transversal del núcleo de la función (por ejemplo: logging, validación, control de acceso).
  • Reutilizar comportamiento en múltiples funciones sin duplicar código.
  • Extender funcionalidades en bibliotecas de terceros.
  • Aplicar principios de programación funcional de manera más limpia y legible.

El ejemplo más simple de decorador

Vamos a comenzar con un decorador básico que imprime un mensaje antes y después de ejecutar una función.


def mi_decorador(func):
    def envoltura():
        print("Antes de ejecutar la función")
        func()
        print("Después de ejecutar la función")
    return envoltura

@mi_decorador
def saludar():
    print("Hola mundo")

saludar()
  

El uso de @mi_decorador es azúcar sintáctica para saludar = mi_decorador(saludar).

Decoradores con argumentos

Para que nuestro decorador sea más flexible, podemos permitirle aceptar argumentos.


def decorador_con_argumentos(func):
    def envoltura(*args, **kwargs):
        print(f"Llamando a {func.__name__} con argumentos: {args}, {kwargs}")
        return func(*args, **kwargs)
    return envoltura

@decorador_con_argumentos
def sumar(a, b):
    return a + b

resultado = sumar(5, 3)
print(f"Resultado: {resultado}")
  

Decoradores anidados (decoradores con parámetros)

También es posible crear decoradores que reciben sus propios parámetros:


def repetir(n):
    def decorador(func):
        def envoltura(*args, **kwargs):
            for _ in range(n):
                func(*args, **kwargs)
        return envoltura
    return decorador

@repetir(3)
def decir_hola():
    print("Hola!")

decir_hola()
  

En este ejemplo, el decorador repite la ejecución de la función decorada n veces.

Preservando metadata con functools.wraps

Al decorar una función, esta pierde su metadata como nombre, docstring, etc. Para evitarlo, usamos functools.wraps:


import functools

def decorador(func):
    @functools.wraps(func)
    def envoltura(*args, **kwargs):
        print("Ejecutando", func.__name__)
        return func(*args, **kwargs)
    return envoltura

@decorador
def saludar():
    """Esta función saluda"""
    print("¡Hola!")

print(saludar.__name__)  # 'saludar'
print(saludar.__doc__)   # 'Esta función saluda'
  

Aplicaciones comunes de decoradores

  • Medición de tiempo de ejecución
  • Memoización y caching
  • Control de acceso/autenticación
  • Validación de entrada
  • Registro de eventos o errores

Ejemplo: medidor de tiempo de ejecución


import time
import functools

def medir_tiempo(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        inicio = time.time()
        resultado = func(*args, **kwargs)
        fin = time.time()
        print(f"{func.__name__} tomó {fin - inicio:.4f} segundos")
        return resultado
    return wrapper

@medir_tiempo
def tarea_lenta():
    time.sleep(1)
    print("Tarea completada.")

tarea_lenta()
  

Decoradores integrados en Python

Python ofrece varios decoradores ya integrados en el lenguaje:

  • @staticmethod: convierte un método en un método estático.
  • @classmethod: permite que un método reciba la clase como primer argumento.
  • @property: convierte un método en un atributo de solo lectura.

Decoradores en frameworks populares

Los decoradores son usados ampliamente en frameworks de Python. Algunos ejemplos:

  • Flask: @app.route() para definir rutas.
  • Django: @login_required para proteger vistas.
  • Pytest: @pytest.mark.parametrize para pruebas con múltiples valores.

Errores comunes al usar decoradores

  • Olvidar retornar la función interna (envoltura).
  • No usar functools.wraps, perdiendo metadata útil.
  • Confundir decoradores con o sin argumentos.
  • No manejar bien los argumentos y kwargs en la envoltura.

Conclusión

Los decoradores son una herramienta poderosa en Python que te permiten escribir código más limpio, reutilizable y expresivo. Aunque su sintaxis pueda parecer compleja al principio, con práctica se vuelven una parte natural del desarrollo Python moderno. Desde bibliotecas populares hasta utilidades personalizadas, dominarlos te hará un desarrollador más competente y versátil.

Te animo a que pruebes escribir tus propios decoradores en distintos contextos. La práctica es la clave para interiorizar este patrón.

Post a Comment

Artículo Anterior Artículo Siguiente