Las funciones decoradoras de Python con parámetros

Las funciones decoradoras de Python con parámetros

Las funciones decoradoras de Python con parámetros - Máster en Python #25

En esta ocasión, te voy a mostrar como utilizar las funciones decoradoras con parámetros.

Para este capítulo, comenzamos con el ejemplo del capítulo anterior.

def a(b):
    def c():
        print(f"El resultado de la operación es: ")
        b()
        print("Operación realizada con éxito.")
    return c

@a
def sumar():
    print(10 + 10)
    
@a
def restar():
    print(10 - 20)

def multiplicar():
    print(45 * 2)

def dividir():
    print(4 / 87)

Estas funciones son poco prácticas, ya que tienen un valor de operación estático (siempre es el mismo resultado).

Con el fin de hacer estas funciones prácticas, podemos añadir parámetros:

def sumar(numero1, numero2):
    print(numero1 + numero2)

Si llamamos a esta función sin utilizar decoradores, no hay problema alguno:

sumar(40,54)
sumar(100,305)
sumar(48,52)

Resultado en la consola

94
405
100

Pero, ¿y si intentamos decorarla?

def a(b):
    def c():
        print("El resultado de la operación es: ")
        b()
        print("Operación realizada con éxito.")
    return c
@a
def sumar(numero1, numero2):
    print(numero1 + numero2)
    
sumar(40,54)
sumar(100,305)
sumar(48,52)

Error en la consola

TypeError: a.<locals>.c() takes 0 positional arguments but 2 were given

El error me está diciendo que en la función interna (la c) que lleva la función decoradora (la a), estoy pasando 2 argumentos posicionales (numero1 y numero2), pero espera 0.

Por lo tanto, ya sabemos donde hay que colocar los parámetros, en la función c, la interna.

def a(b):
    def c(numero1,numero2):
        print(f"El resultado de la operación es: ")
        b()
        print("Operación realizada con éxito.")
    return c
@a
def sumar(numero1, numero2):
    print(numero1 + numero2)

sumar(40,54)
sumar(100,305)
sumar(48,52)

Error en la consola

b()
TypeError: sumar() missing 2 required positional arguments: 'numero1' and 'numero2'

Vaya, otro error. Ahora, el error está en la función b(), la función parámetro. Dice que faltan 2 argumentos posicionales requeridos. Para solucionar esto, le añadimos los dos parámetros y listo.

def a(b):
    def c(numero1,numero2):
        print(f"El resultado de la operación es: ")
        b(numero1, numero2)
        print("Operación realizada con éxito.")
    return c
@a
def sumar(numero1, numero2):
    print(numero1 + numero2)
    
sumar(40,54)

Resultado en la consola

El resultado de la operación es:
94
Operación realizada con éxito.

Utilizar *args en las funciones decoradoras de Python

El resultado está muy bien, pero ¿y si queremos utilizar el decorador en funciones que tengan un número indeterminado de parámetros?

Supongamos que en la función resta queremos operar con 4 números y no con dos, pero en la de suma, queremos seguir operando con 2.

Con el fin de no tener que crear varias funciones decoradoras para utilizar lo mismo (lo que les haría perder todo el sentido), podemos utilizar el anteriormente explicado *args.

def a(b):
    def c(*args):
        print(f"El resultado de la operación es: ")
        b(*args)
        print("Operación realizada con éxito.")
    return c
@a
def sumar(numero1, numero2):
    print(numero1 + numero2)
    
@a
def restar(numero1, numero2, numero3, numero4):
    print(numero1 - numero2 - numero3 - numero4)
    
sumar(30,50)
restar(40,54,60,43)

Utilizar *kwargs en las funciones decoradoras de Python

Pasemos a otro ejemplo para ver la utilidad de emplear **kwargs en las funciones decoradoras de Python.

Aquí hay varias funciones que esperan diferentes argumentos. Algunas dos y una de ellas solo uno.

Pues bien, partiendo de la base, necesitamos poner *args si queremos decorar estas funciones.

import math

def a(b):
    def c(*args):
        print("Empieza el cálculo...")
        b(*args)
        print("Operación realizada con éxito.")
    return c

@a
def area_rectangulo(base, altura): 
    print(f"El área del rectángulo es: {base * altura}.")
@a
def area_triangulo(base, altura):
    print(f"El área del rectángulo es: {base * altura / 2}")
@a
def area_circulo(radio):
    print(f"El área del círculo es {math.pi * radio ** 2}")
    
area_rectangulo(10,40)

Resultado en la consola

Empieza el cálculo…
El área del rectángulo es: 400.
Operación realizada con éxito.

Ahora, intenta utilizar argumentos de clave para especificar la llamada, por ejemplo esto:

area_rectangulo(altura=40,base=10)

Error en la consola

area_rectangulo(altura=40,base=10)
TypeError: a.<locals>.c() got an unexpected keyword argument 'altura'

Nos suelta un error diciendo que no se esperaba un argumento de clave, claro, porque se lo estoy pasando a *args. *args, como bien expliqué anteriormente, solo recibe argumentos posicionales.

Es aquí, donde hay que añadir la funcionalidad extra de **kwargs a nuestra función decoradora. Esto permitirá el uso de argumentos de clave en las funciones decoradas.

import math

def a(b):
    def c(*args, **kwargs):
        print("Empieza el cálculo...")
        b(*args, **kwargs)
        print("Operación realizada con éxito.")
    return c

@a
def area_rectangulo(base, altura): 
    print(f"El área del rectángulo es: {base * altura}.")
@a
def area_triangulo(base, altura):
    print(f"El área del rectángulo es: {base * altura / 2}")
@a
def area_circulo(radio):
    print(f"El área del círculo es {math.pi * radio ** 2}")
    
area_rectangulo(altura=40,base=10)
area_rectangulo(base=6,altura=10)
area_circulo(radio=2)

Resultado en la consola

Empieza el cálculo…
El área del rectángulo es: 400.
Operación realizada con éxito.
Empieza el cálculo…
El área del rectángulo es: 60.
Operación realizada con éxito.
Empieza el cálculo…
El área del círculo es 12.566370614359172
Operación realizada con éxito.

No te pierdas nada de todo el contenido que tengo sobre Python.

Ejercicio de Python con funciones decoradoras y parámetros

En esta sección, tienes un ejercicio relacionado con el temario, sobre las funciones decoradoras con el uso de parámetros. Las soluciones estarán disponibles en el siguiente capítulo.

def muestra_datos(**kwargs):
    claves = tuple(kwargs.keys())
    valores = tuple(kwargs.values())
    print(f"El {claves[0]} es {valores[0]}, sus {claves[1]} son {valores[1]} y tiene {valores[2]} años de {claves[2]}." )
    
usuario1 = {"nombre":"Javier", "apellidos":"Gómez de la barca", "edad":"27"}

muestra_datos(**usuario1)

Crea una función decoradora que, mediante este código, saque unos mensajes como estos en la consola:

Resultado en la consola

Leyendo el diccionario…
El nombre es Javier, sus apellidos son Gómez de la barca y tiene 27 años de edad.
Operación realizada con éxito.

Solución del ejercicio de Python del capítulo anterior

Esta es mi solución:

def funcion_decoradora(funcion_parametro):
    def funcion_interna():
        print("-> ITERADOR DE LISTAS <-")
        print("Aquí tienes todos los elementos de la lista:")
        funcion_parametro()
        print("La lista se recorrió con éxito.")
    return funcion_interna

colores = ["rojo", "azul", "verde", "amarillo"]

@funcion_decoradora
def recorre_lista():
    for i in colores:
        print(f"El valor es: {i}.")
        
recorre_lista()

Resultado en la consola

-> ITERADOR DE LISTAS <-
Aquí tienes todos los elementos de la lista:
El valor es: rojo.
El valor es: azul.
El valor es: verde.
El valor es: amarillo.
La lista se recorrió con éxito.

Un comentario en «Las funciones decoradoras de Python con parámetros»

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

curso Java Entrada anterior El bucle for de Java
Ejercicios cobol Entrada siguiente Ejercicios para practicar con COBOL – Parte 1