Las funciones decoradoras de Python – Máster en Python #24
En esta ocasión, te traigo un tema nuevo en el curso, empezamos con las funciones decoradoras de Python.
En este capítulo te enseñaré la base fundamental de estas funciones decoradoras y en el siguiente, te mostraré como utilizarlas con argumentos, los cuales añaden un extra de dificultad, por este motivo, prefiero dividirlo en dos partes.
¿Qué son las funciones decoradoras de Python?
Las funciones decoradoras de Python, son funciones especiales que nos ayudarán a «decorar» el código de ciertas funciones. Se trata de emplear ciertas funcionalidades de una función en otras.
Esto quedará más claro con los ejemplos de más abajo. Si ves que te cuesta entenderlo, en el vídeo tienes explicaciones muy detalladas.
Estructura de una función decoradora de Python
La estructura de una función decoradora de Python se basa en 3 funciones, las cuales, llamaremos «a», «b» y «c» para simplificar las explicaciones (estos nombres no son obligatorios).
La función «a», va a ser la que reciba como parámetro la función «b». A raíz de esto, la función «a», devolverá con un «return» el valor de llamar a la función «c».
Esto parece una locura, pero verás que es más sencillo de lo que parece y que no es tan complicado.
Sintaxis de una función decoradora de Python
Esta es la sintaxis de una función decoradora de Python.
def a(b): def c(): #código de la función c return c
Otra forma de representar esto es la siguiente. Aquí, puedes ver que la función «a» obtiene la función «b» como parámetro y devuelve la función «c».
def a(b) -> c
Ejemplo práctico con funciones decoradoras de Python
Pasemos a la parte práctica del capítulo. Con este ejemplo, podrás entender mucho mejor esa sintaxis de las funciones decoradoras de Python.
Voy a crear cuatro funciones de operaciones aritméticas. Hasta aquí, son funciones normales y corrientes.
Soy consciente de que no son funciones útiles, pero quiero simplificar el concepto de las funciones decoradoras al máximo, después, ya habrá tiempo de complicarlo.
def sumar(): print(10 + 10) def restar(): print(10 - 20) def multiplicar(): print(45 * 2) def dividir(): print(4 / 87)
Ahora, supón que las quieres mejorar y añadirles alguna frase antes de realizar la operación y otra después.
def sumar(): print("El resultado de la operación es: ") print(10 + 10) print("Operación realizada con éxito.") def restar(): print("El resultado de la operación es: ") print(10 - 20) print("Operación realizada con éxito.") def multiplicar(): print("El resultado de la operación es: ") print(45 * 2) print("Operación realizada con éxito.") def dividir(): print("El resultado de la operación es: ") print(4 / 87) print("Operación realizada con éxito.")
El código de estas funciones se está repitiendo mucho, tanto el primer print() como el tercero, son exactamente iguales en las 4 funciones ¿Y si en lugar de 4 tienes 100 funciones de este tipo?
Aquí es donde entran en juego las funciones decoradoras.
Esto es una función decoradora, la función llamada «a».
A esta se le pasa como parámetro una función llamada «b» (ahora explico esto).
Después, vamos a escribir la función interna, la «c». Esta lleva una parte de código que se va a ejecutar antes de llamar a la función decorada (que será suma, resta…). Por lo tanto, imprimirá primero «El resultado de la operación es: «.
Después de esto, llama a la función «b», la cual no es más que una llamada a cualquier función que decoremos con «a». Ejecuta el código de la función decorada y finalmente, ejecutaría el último print().
Para que este tinglado funcione, hay que devolver al programa la función interna con un «return».
Puede que no lo entiendas muy bien, si es así, te recomiendo ver el vídeo. Seguro que te ayuda a entenderlo mejor.
def a(b): def c(): print("El resultado de la operación es: ") b() print("Operación realizada con éxito.") return c
Ahora, para decorar las funciones que queramos, le añadimos el símbolo «@» y el nombre de la función decoradora, que es la «a». Yo la voy a aplicar solo a sumar() y a restar(), así verás la diferencia de usarla o no usarla.
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(): print(10 + 10) @a def restar(): print(10 - 20) def multiplicar(): print(45 * 2) def dividir(): print(4 / 87)
Llamo a la función sumar():
sumar()
Resultado en la consola
El resultado de la operación es:
20
Operación realizada con éxito.
Ahora, llamo a dividir, la cual, no está decorada.
dividir()
Resultado en la consola
0.04597701149425287
Podemos hacer muchas cosas con las funciones decoradoras. Seguiremos utilizándolas en el curso para que veas ejemplos de uso mucho más prácticos que estos.
No te pierdas nada de todo el contenido que tengo sobre Python.
Ejercicio de Python con funciones decoradoras
En esta sección, tienes un ejercicio relacionado con el temario, sobre las funciones decoradoras. Las soluciones estarán disponibles en el siguiente capítulo.
La siguiente función sirve para iterar listas completas e ir mostrando sus valores en la consola.
colores = ["rojo", "azul", "verde", "amarillo"] def recorre_lista(): for i in colores: print(f"El valor es: {i}.") recorre_lista()
Resultado en la consola
El valor es: rojo.
El valor es: azul.
El valor es: verde.
El valor es: amarillo.
El reto para este ejercicio, es que crees una función decoradora que dé una salida en la consola, parecida o igual que esta:
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.
Es importante recalcar que, como requisito, no puedes modificar nada de la función «recorre_lista()».
Soy consciente de que sin parámetros, la función pierde todo el sentido, ya que no se le pueden pasar diferentes listas para iterar. Sin embargo, en el siguiente capítulo, te enseñaré a utilizar parámetros en las funciones decoradoras y mejoraremos esta.
Soluciones de los ejercicios de Python del capítulo anterior
- Aquí lo único que esta mal, son los dos puntos (:) en lugar de (=). Debe quedar así:
def __init__(self, nombre="vacío", apellidos="vacío", edad="N/A"):
- Pues esto es lo mínimo que tenías que hacer en este ejercicio. Si has añadido parámetros extra, perfecto.
class Vehiculo: def __init__(self, ruedas="4", puertas="5", color="negro", asientos="5"): self.ruedas = ruedas self.puertas = puertas self.color = color self.asientos = asientos def describe(self): print(f"Ruedas: {self.ruedas}.") print(f"Puertas: {self.puertas}.") print(f"Color: {self.color}.") print(f"Asientos: {self.asientos}.")
- Para que la instancia sea con todos los valores por defecto del __init__, hay que omitir declarar argumentos en la instanciación.
#instancia de Vehiculo vehiculo_1 = Vehiculo() #llamada al método vehiculo_1.describe()
Resultado en la consola
Ruedas: 4.
Puertas: 5.
Color: negro.
Asientos: 5.
- Aquí, tenías que especificar algunos argumentos en la instanciación:
#instancia de Vehiculo vehiculo_2 = Vehiculo(color="rojo", puertas="3", asientos="4") #llamada al método vehiculo_2.describe()
Resultado en la consola
Ruedas: 4.
Puertas: 3.
Color: rojo.
Asientos: 4.