2 usuarios conectados

Funciones. Decoradores en Python

Comparte esto

Ahora vamos a explorar los decoradores en Python, una característica poderosa y elegante que te permite modificar o extender el comportamiento de funciones o métodos de una manera legible y reutilizable.

¿Qué son los Decoradores?

En esencia, un decorador es una función que toma otra función como argumento, la envuelve con alguna funcionalidad adicional y devuelve la función envuelta. Los decoradores se aplican a las funciones utilizando la sintaxis @nombre_del_decorador justo antes de la definición de la función que se va a decorar.

Sintaxis Básica:

Python
 
def mi_decorador(func):
    def funcion_envolvente(*args, **kwargs):
        # Código a ejecutar ANTES de la función original
        print("Antes de llamar a la función.")
        resultado = func(*args, **kwargs)  # Llamada a la función original
        # Código a ejecutar DESPUÉS de la función original
        print("Después de llamar a la función.")
        return resultado
    return funcion_envolvente

@mi_decorador
def mi_funcion():
    print("¡Soy la función original!")

mi_funcion()

Salida:

Antes de llamar a la función.
¡Soy la función original!
Después de llamar a la función.

Desglosando el Ejemplo:

  1. mi_decorador(func): Esta es la función decoradora. Toma una función (func) como argumento.
  2. funcion_envolvente(*args, **kwargs): Dentro del decorador, se define una función interna (funcion_envolvente). Esta función envolvente es la que realmente se ejecutará cuando llamemos a la función decorada. Utiliza *args y **kwargs para poder aceptar cualquier número y tipo de argumentos que la función original pueda tener.
  3. print("Antes de llamar a la función."): Aquí se puede agregar código que se ejecute antes de que se llame a la función original.
  4. resultado = func(*args, **kwargs): Se llama a la función original (func) pasando los argumentos que recibió la función envolvente.
  5. print("Después de llamar a la función."): Aquí se puede agregar código que se ejecute después de que la función original haya terminado.
  6. return resultado: La función envolvente devuelve el resultado de la función original.
  7. return funcion_envolvente: El decorador devuelve la función envolvente.
  8. @mi_decorador: Esta sintaxis "decora" la función mi_funcion. Es equivalente a hacer: mi_funcion = mi_decorador(mi_funcion). Cuando llamas a mi_funcion(), en realidad estás llamando a la funcion_envolvente que devuelve mi_decorador.

Usos Comunes de los Decoradores:

Decoradores con Argumentos:

A veces, quieres que tu decorador acepte argumentos. Para hacer esto, necesitas crear una función que envuelva al decorador real.

Python
 
def repetir(num_veces):
    def decorador_repetir(func):
        def funcion_envolvente(*args, **kwargs):
            for _ in range(num_veces):
                print("Ejecutando la función...")
                resultado = func(*args, **kwargs)
            return resultado
        return funcion_envolvente
    return decorador_repetir

@repetir(num_veces=3)
def saludar(nombre):
    print(f"¡Hola, {nombre}!")

saludar("Mundo")

Salida:

Ejecutando la función...
¡Hola, Mundo!
Ejecutando la función...
¡Hola, Mundo!
Ejecutando la función...
¡Hola, Mundo!

Aquí, repetir es una función que toma num_veces como argumento y devuelve el decorador real (decorador_repetir). Luego, @repetir(num_veces=3) llama a repetir con 3 y el decorador resultante se aplica a saludar.

Múltiples Decoradores:

Puedes aplicar múltiples decoradores a una misma función. Se aplicarán de abajo hacia arriba (el decorador más cercano a la definición de la función se aplica primero).

Python
 
def hacer_negrita(func):
    def envolvente():
        return "<b>" + func() + "</b>"
    return envolvente

def hacer_cursiva(func):
    def envolvente():
        return "<i>" + func() + "</i>"
    return envolvente

@hacer_negrita
@hacer_cursiva
def obtener_texto():
    return "Texto"

texto_formateado = obtener_texto()
print(texto_formateado)  # Salida: <b><i>Texto</i></b>

En este caso, obtener_texto primero se decora con hacer_cursiva, y luego el resultado se decora con hacer_negrita.

Decoradores para Métodos de Clase:

Los decoradores también se pueden aplicar a los métodos dentro de las clases. El primer argumento de un método de clase siempre es self, y el decorador debe tener en cuenta esto.

Python
 
class Ejemplo:
    def mi_decorador_metodo(func):
        def envolvente(self, *args, **kwargs):
            print("Antes de llamar al método.")
            resultado = func(self, *args, **kwargs)
            print("Después de llamar al método.")
            return resultado
        return envolvente

    @mi_decorador_metodo
    def mi_metodo(self):
        print("¡Soy el método!")

obj = Ejemplo()
obj.mi_metodo()

Salida:

Antes de llamar al método.
¡Soy el método!
Después de llamar al método.

Los decoradores son una herramienta poderosa para escribir código más limpio, modular y reutilizable en Python. Permiten separar la lógica de la funcionalidad principal de una función de la lógica auxiliar (como logging, seguridad, etc.).