Funciones: base de los decoradores

6/25

Lectura

El concepto de decorador en Python es algo que podr铆amos ubicar en un nivel 鈥渋ntermedio鈥 en el manejo del lenguaje, por lo que es buena idea que tengas una base s贸lida, sobre todo en cuanto a funciones al momento de profundizar e implementarlas.

Los decoradores son una forma sencilla de llamar funciones de orden mayor, es decir, funciones que toman otra funci贸n como par谩metro y/o retornan otra funci贸n como resultado. De esta forma un decorador a帽ade capacidades a una funci贸n sin modificarla.

Un ejemplo de esto son las llantas de un autom贸vil. Si les colocas cadenas para la nieve, el autom贸vil a煤n puede andar y adem谩s extiende su funcionalidad para conducirse en otros terrenos.

Recordando sobre funciones

Antes de abordar el tema de decoradores haremos un peque帽o repaso por las funciones, las cuales retornan un valor ante la entrada de un argumento.

Analicemos este sencillo ejemplo donde una funci贸n que multiplica un n煤mero se eleva a la tercera potencia:

def elevar_cubo(numero):
	return numero * numero * numero

Si damos como argumento el n煤mero 3, entonces tendremos como salida el n煤mero 27 al ejecutar la funci贸n:

>>> elevar_cubo(3)
27

Funciones como objetos de primera-clase

Otro concepto importante a tener en cuenta es que en Python las funciones son objetos de primera-clase, es decir, que pueden ser pasados y utilizados como argumentos al igual que cualquier otro objeto (strings, enteros, flotantes, listas, etc.).

Veamos un ejemplo donde definimos 3 diferentes funciones que utilizaremos de manera conjunta:

def presentarse(nombre):
	return f"Me llamo {nombre}"

def estudiemos_juntos(nombre):
	return f"隆Hey {nombre}, aprendamos Python!"

def consume_funciones(funcion_entrante):
	return funcion_entrante("David")

Las primeras dos funciones son obvias en su resultado, donde nos mostrar谩n un mensaje con el valor de la variable nombre. La tercera funci贸n puede ser m谩s compleja de predecir, ya que toma otra funci贸n como entrada. Veamos que pasa cuando colocamos una funci贸n como atributo:

>>> consume_funciones(presentarse)
'Me llamo David'

>>> consume_funciones(estudiemos_juntos)
'隆Hey David, aprendamos Python!'

Pongamos atenci贸n en c贸mo la funci贸n consume_funciones() se escribe con par茅ntesis para ser ejecutada, mientras que la funci贸n presentarse y estudiemos_juntos solo hace referencia a estas.

Funciones anidadas

Al igual que los condicionales y bucles tambi茅n puedes colocar funciones dentro de otra funci贸n.

T贸mate un minuto para analizar el siguiente c贸digo e inferir cu谩l ser谩 el resultado de salida:

def funcion_mayor():
	print("Esta es una funci贸n mayor y su mensaje de salida.")

	def librerias():
		print("Algunas librer铆as de Python son: Scikit-learn, NumPy y TensorFlow.")

	def frameworks():
		print("Algunos frameworks de Python son: Django, Dash y Flask.")

	frameworks()
	librerias()

Si llamamos a la funci贸n funcion_mayor tendremos la siguiente salida:

>>> funcion_mayor()
Esta es una funci贸n mayor y su mensaje de salida.
Algunos frameworks de Python son: Django, Dash y Flask.
Algunas librer铆as de Python son: Scikit-learn, NumPy y TensorFlow.

Debemos considerar que las funciones anidadas dentro de funcion_mayor no se ejecutan hasta que se llama a esta primera, siendo muestra del scope o alcance de las funciones. Si las llamamos obtendremos un error


En la siguiente lectura entraremos al concepto de decoradores, setters y getters, pues al entender mejor las funciones ser谩 m谩s f谩cil asimilar su uso en la implementaci贸n del encapsulamiento.

Aportes 37

Preguntas 2

Ordenar por:

驴Quieres ver m谩s aportes, preguntas y respuestas de la comunidad? Crea una cuenta o inicia sesi贸n.

Amigos, adjunto link donde se explica demasiado bien una funci贸n decoradora, saludos.
https://www.youtube.com/watch?v=DQXm6bIZgvk

Aqu铆 dejo mi aporte y resumen de la clase:

<h3>Funciones: base de los decoradores</h3>

Antes de empezar a hablar de decoradores, recordemos que una funci贸n retorna un valor ante la entrada de un argumento:

def al_cuadrado(numero):
  return numero ** 2

Esta funci贸n recibe el arguemnto numero y a partir de eso retorna (return) el argumento numero elevado al cuadrado (numero ** 2).

Ahora, probemos esta funci贸n. Si damos como par谩metro el n煤mero 4, tendremos como salida el valor n煤mero 16 (es el valor que nos retorna):

>>> al_cuadrado(4)
16

Si no sabes o no te queda claro la diferencia entre argumentos y par谩metros de las funciones, te invito a que vayas a este Colab (https://colab.research.google.com/drive/1VyOB8nRWRTscc7k_V-66NIlfKZZZ8AuW?usp=sharing) y que vayas hacia las secci贸n Funciones y dentro vayas a la secciones Argumentos y Par谩metros de las funciones y Los argumentos NO son lo mismo que los par谩metros. All铆 explico esta confusi贸n qu茅 suele ocurrir en programaci贸n.


Los decoradores son una forma sencilla de llamar funciones de orden mayor, qu茅 b谩sicamente son funciones que toman otra funci贸n como par谩metro y/o retornan otra funci贸n como resultado. Entonces, un decorador le a帽ade capacidades a una funci贸n sin tener que modificarla.

Un ejemplo son las llantas de un coche, a las cu谩les les puede a帽adir cadenas para la nieve. No modificas el comportamiento de que a煤n pueda andar, porqu茅 s铆 puede, y adem谩s extiendes su funcionalidad a otros terrenos.

<h3>Funciones como objetos de primera clase</h3>

C贸mo vimos en el curso anterior, las funciones son objetos de primera clase, lo que significa que puede ser pasados y usados como argumentos al igual que cualquier otro objeto(int, float, str). Veamos un ejemplo d贸nde utilizamos 3 funciones en conjunto:

def presentarse(nombre):
  return f'隆Hola! Me llamo {nombre}.'

def aprender(nombre):
  return f'隆{nombre}, aprendamos Python!'

def usar_funciones(funcion):
  return funcion("Ignacio") #establecemos el par谩metro que tomar谩n las primeras 2 funciones

Las primeras 2 funciones nos retornan strings. Pero la 煤ltima, se comportar谩 diferente seg煤n la funci贸n que le pasemos como par谩metro. Veamos c贸mo se puede usar:

>>> print(usar_funciones(presentarse))
隆Hola! Me llamo Ignacio.

>>> print(usar_funciones(aprender))
隆Ignacio, aprendamos Python!

C贸mo ves, nos imprime los mensajes que retornaban las 2 primeras funciones con el nombre correspondiente. F铆jate que para referirnos como par谩metros a las 2 primeras funciones no ponemos par茅ntesis, sino que las escribimos as铆 presentarse y aprender. Adem谩s, autom谩ticamente reciben como par谩metro el nombre "Ignacio", qu茅 es el argumento nombre de las funciones.


<h3>Funciones anidadas</h3>

Esto tambi茅n lo vimos en el curso anterior, d贸nde pod铆amos meter funciones dentro de otras funciones al igual que las condicionales:

numero = 5

if numero > 1:
  if numero % 2 == 0:
    print("Este n煤mero es compuestoo.")

  else:
    print("Este n煤mero es primo.")

else:
  print("Este n煤mero es igual o menor a 1."
>>> (ejecutamos)
Este n煤mero es primo

Ahora veamos c贸mo aplica este concepto para las funciones:

def funcion_padre(numero):

  def doble():
    resultado = numero * 2
    print('El doble de {numero} es {resultado}.')

  def al_cuadrado():
    resultado = numero ** 2
    print('{numero} al cuadrado es {resultado}.')

En esta funci贸n (funcion_padre()), la cu谩l recibe el argumento numero, dentro hay otras 2 funciones (doble y al_cuadrado), las cuales sirven para calcular el doble y/o el cuadrado del argumento recibido, respectivamente. Ahora llamaremos a funcion_padre() para ver qu茅 cosas podemos hacer:

>>> funcion_padre(5)

No se ejecuta nada, y eso es porque debemos agregar a funcion_padre() que las 2 funciones anidadas deben ejecutarse.

def funcion_padre(numero):

  def doble():
    resultado = numero * 2
    print(f'El doble de {numero} es {resultado}.')

  def al_cuadrado():
    resultado = numero ** 2
    print(f'{numero} al cuadrado es {resultado}.')

  doble() #se ejecuta doble() 
  al_cuadrado() #se ejecuta al_cuadrado()

>>> funcion_padre(5)
El doble de 5 es 10.
5 al cuadrado es 25.

Ahora s铆, funcion_padre() funciona, valga la redundancia. C贸mo vemos, las funciones doble() y al_cuadrado() no se ejecutan hasta que primero se llama a funcion_padre(). Esto sigue las reglas del scope o alcance. En este caso, no podemos a acceder a las funciones internas sin antes acceder a la funci贸n global que contiene dichas funciones internas.


def decorator_function(func_param):
    
    def internal_function():
        # additional actions that decorate 
        print("Let's to do a calculation")
        
        func_param()
        # additional actions that decorate
        print('We have finished the calculation ')
    return internal_function

@decorator_function
def suma():
    print(15+20)
    

@decorator_function
def resta():
    print(30-10)
    

suma()
resta()

Para el que le interese, si tenemos en cuenta que dentro de una funci贸n puedes definir tus propias funciones y a su vez puedes traer funciones como par谩metros, 驴Qu茅 pasa si definimos una funci贸n interna con el mismo nombre que un par谩metro? Nota: No es bueno hacerlo, para nada.

驴Resultado? Es como si reasignara el valor de una variable porqu茅 al momento de definir la funci贸n interna sobrescribe a la variable que tra铆a la funci贸n externa.

Este fue el c贸digo que use:

def funcion1(funcion2):
    print('Funcion Externa N潞1')

    funcion2()

    def funcion2():
        print('Funcion Externa N潞1, Funcion Interna N潞2')

    funcion2()

def funcion2():
    print('Funcion Externa N潞2')

Y este el resultado:

>>>funcion1(funcion2)
Funcion Externa N1
Funcion Externa N2
Funcion Externa N1, Funcion Interna N2

驴Conclusi贸n? Evita repetir nombres en tus variables, te ahorrar谩s muchos problemas

En el ejemplo de Funciones c贸mo objetos de primera-clase la funci贸n consume_funciones debe estar de la siguiente manera:

def consume_funciones(funcion_entrante):
	return funcion_entrante("David")

En mi opini贸n, no es un tema de decoradores, en el tema menciona principalmente sobre las funciones y el papel importante que tienen y explica a groso modo que son los decoradores, que en el siguiente curso es donde se abordar谩 ese tema.
馃槂

En el ejemplo de la funci贸n elevar_cubo, no entend铆a por qu茅 se escrib铆a numbernumbernumber (Porque ** es para potencias y * solo para multiplicar), adem谩s el par谩metro en la funci贸n se llama numero, no number.
Creer铆a que tendr铆a que ser:

Podemos ver las funciones como una peque帽a f谩brica, donde le ingresamos un producto y esta fabrica nos devuelve algo fabricado como resultado.

Cuando pasamos una funcion como parametro a otra funcion lo que en realidad hacemos es, pasarle una 鈥渃aja鈥 chiquita como producto a otra caja m谩s grande.

Y esta caja grande nos devolver谩 un producto procesado. O en nuestro caso un valor.

En el siguiente enlace puedes alcarar mas duduas referente al tema de decoradores en python

https://www.apsl.net/blog/2009/08/31/decoradores-en-python/

Un objeto de primera clase, es simplemente aquel que podemos pasar como argumento a una funci贸n(desde listas hasta objetos m谩s complejos).

Una funci贸n decoradora se encarga de a帽adir funcionalidades extra a una funci贸n, y requiere de tres funciones para existir.

Funci贸n A, que es la que ejecuta nuestro c贸digo, funci贸n B, la cual decoraremos y pasamos como argumento a nuestro c贸digo y la funci贸n C que es la nueva funci贸n que retornamos.

def decorator_function(param_function):
	
	def funcion_nueva():
		print("esto es una operaci贸n matem谩tica")
		param_function()
	
	return function_nueva

def suma(a, b):
	return a + b

def resta(a, b):
	return a - b

Y ahora se imprimir谩 un mensaje antes de iniciar cualquier funci贸n que pasemos como parametro.

Un decorador es el nombre de un patr贸n de dise帽o. Los decoradores alteran de manera din谩mica la funcionalidad de una funci贸n, m茅todo o clase sin tener que hacer subclases o cambiar el c贸digo fuente de la clase decorada. En el sentido de Python un decorador es algo m谩s, incluye el patr贸n de dise帽o, pero va m谩s all谩, Bruce Eckel los asimila a las macros de Lisp. Los decoradores y su utilizaci贸n en nuestros programas nos ayudan a hacer nuestro c贸digo m谩s limpio, a autodocumentarlo y, a diferencia otros lenguajes, no requieren que nos aprendamos otro lenguaje de programaci贸n distingo (c贸mo pasa con las anotaciones de Java por ejemplo).

def funcionMayor():
 print('Esta es una funci贸n mayor y su mensaje de salida.')

 def librerias():
      print('Algunas librer铆as de Python son: Scikit-learn, NumPy y TensorFlow.')
    
 def frameworks():
      print('Algunos frameworks de Python son: Django, Dash y flask.')
    
 frameworks() #Llama a la funci贸n frameworks para su impresi贸n
 librerias() #Llama a la funci贸n librerias para su impresi贸n

funcionMayor() #Llama a la funci贸n 'FUNCIONMAYOR' para su impresi贸n```

Ejemplo de decoradores:

Ejemplo de uso decoradores: Para ello necesitaremos funcion "A", que reciba como parametro otra funcion "B" para devolver otra funcion "C".

Ejemplo codigo:



def decorador(func): #A, #B

    def nueva_funcion():
      print("Ejecutando la funcion")
            #Aqui es donde se agrega la logica que va a ser a帽adida a la funcion
            func()
            print("Se ejecuto la funcion")

    return nueva_funcion #C

@decorador #Aqui es donde se le agrega la decoracion, la funcion que queramos
def saluda(): #C
    print("Hola mundo")

saluda()

El profe david tiene una clase de otro curso donde explica, los decoradores aqui se las dejo por si quieren checar.

En este apartado vemos lo siguiente:

  • 驴Qu茅 son decoradores?
  • 驴En qu茅 me ayudan las funciones de primera clase?
  • 驴En qu茅 consisten las funciones anidadas?

Una duda que me salt贸 es en qu茅 casos podr铆a utilizar funciones anidadas?

No s茅 si al decir 鈥渄ecoradores鈥 se est谩 refiriendo al patr贸n de dise帽o Decorator jaja, lo ver茅 en la siguiente clase, pero la parte de una funci贸n que retorna a otra, si vienes de JavaScript, puedes verla como un callback pues b谩sicamente eso es, tu le puedes mandar la referencia de una funci贸n a otra para que est谩 la ejecute internamente a modo de callback 馃槃

De nuevo una lectura que deber铆a ser una explicaci贸n en video鈥

Para profundizar en el tema de decoradores y funciones anidadas, les recomiendo el curso profesional de Python, el profesor Facundo explica de manera muy sencilla estos temas 馃槂

dejo un peque帽o experimento con l que entendi como funciona el decorador.

def sumar(a,b):
    return a+b
def raiz_cuadrada(valor):
    return valor**(1/2)
def pitagoras(funcion,a,b):
    return raiz_cuadrada(funcion(a**2,b**2))

print(pitagoras(sumar,2,3))

al correr esto desde la consola python, funciona bien. Pero al correrlo desde la terminar. el resultado es 1. Agradezco si alguien me da una luz del porque sucede esto.

estructura:

<code> 
def funcion_decorador(funcion):
	def funcion_interna():
		pass
	return funcion_interna

Los decoradores se ven de una manera muy clara en el curso de Python Avanzado.
Para entender decoradores se pueden revisar al menos estas clases de Platzi, sin embargo, en el m贸dulo se ven varios ejemplos muy claros (estos est谩n en otras clases del mismo m贸dulo):

Espero les sea de utilidad compa帽eros y a nunca parar de aprender.

def funcion_mayor():
	print("Esta es una funci贸n mayor y su mensaje de salida.")

	def librerias():
		print("Algunas librer铆as de Python son: Scikit-learn, NumPy y TensorFlow.")

	def frameworks():
		print("Algunos frameworks de Python son: Django, Dash y Flask.")

	frameworks()
	librerias()

El concepto de decorador en Python es algo que podr铆amos ubicar en un nivel 鈥渋ntermedio鈥 en el manejo del lenguaje, por lo que es buena idea que tengas una base s贸lida, sobre todo en cuanto a funciones al momento de profundizar e implementarlas.

funcion mayor puede tener funciones dentro que se ejecutaran cuando se llama al mayor porque se encuentra solamente dentro de su scope.

El Scope es el alcance de la funci贸n, hasta donde tiene acceso la funci贸n y dependiendo de donde se encuentre su nivel varia el llamado de la misma. El Scope es diferente dependiendo el lenguaje de programaci贸n aun tienen una similitud.

Forma r谩pida de Recordar

a(b) 鈫 c

a, b, c son funciones.
a recibe a b como par谩metro y retorna c

Dejo un ejemplo de decoradores, espero le sirva a la comunidad

<def decorador(func2): #definimos el decorador con la funci贸n argumento func2
    def nueva_funcion(self, presentacion): #definimos la nueva funci贸n con los parametros que llevara la funcion func2
        print("El profesor comenta:") #codigo del decorador
        func2(self, presentacion) #Agregamos los parametros con los que trabaja 
    return nueva_funcion #regresa la nueva funcion 

class profesor(object): #se crea la clase heraedando object
    def __init__(self, nombre): #Constructor con el atributo nombre
        self.nombre = nombre 
    
    @decorador #se coloca el decorador antes del metodo
    def ensena(self, presentacion): #Metodo ense帽ar del objeto profesor
        self.presentacion = presentacion
        print(presentacion) #imprime el mensaje del objeto
        print("Hoy aprenderemos a programar en Python")

utonio = profesor("Utonio") #instanciamos
utonio.ensena("Bienvenido a tu curso de programaci贸n") # al llamar al metodo ensena a帽adira el decorador
#es decir en el decorador se debe incluir la instanciaci贸n self y el argumento presentaci贸n
# para nueva funci贸n y func2> 
<h3>Funci贸n decoradora</h3>

Funci贸n que se le pasa por parametro una funci贸n y devuelve otra funci贸n diferente
decorator_function: funcion decoradora
externa

def  funcion_decoradora(funcion_por_parametro):
        
        def funcion_interna():
            print('Aqui hacemos cosas')
            print('Acciones randoom')
            print('Tambien aqui usamos nuestra funcion externa')
            funcion_por_parametro(1, 2)
            print('Terminado')
        
        #Ten cuidado con la identacion
        return funcion_interna

#Para llamarla se puede hacer de dos formas

#-----primera------
def suma(num1, num2)
        return print(num1 + num2)

p = funcion_decoradora(suma)
p()
#-----segunda-------
#El arroba le dice que suma sera el parametro de la funcion decoradora y cada vez que llamemos a suma se comportara como si escribieramos la primera opci贸n
@funcion_decoradora
def suma(num1, num2)
        return print(num1 + num2)
suma()

Decoradores
Funciones que toman otra funci贸n c贸mo par谩metro y/o retornan otra funci贸n como resultado.

Funciones anidadas
Recursividad. Funciones dentro de funciones.

Los argumentos son las variables con las que trabajar谩 la funci贸n, mientras que los par谩metros son los valores que le asignaremos a esas variables.

defines las funciones pero no las llamas, dentro de su scope claro

Alguien sabe por que solo imprime la primera si llamo a la funcion mayor, cuando llamo la funcion librerias o frameworks me dice que no estan definidas...

def funcion_mayor():
	print("Esta es una funcion mayor")

	def librerias():
	    print("Algunas librerias de python son... Tkinter, TensorFlow")

	def frameworks():
	    print("Algunos framework de python son... Django dash y flask")	

funcion_mayor()```
print('Funciones b谩sicas: ')
def presentar(nombre):
    print(f'Me llamo {nombre}')

presentar('David')

def estudiemos_juntos(nombre):
    print(f'Hey {nombre}, aprendamos Python!')

estudiemos_juntos('Edgar')

#TOMA UNA FUNCI脫N COMO ENTRADA 
def consume_funciones(funcion_entrante):
    return funcion_entrante('Luis') #Este nombre se asignara a los dos llamados siguientes

print('\nFunci贸n de funciones: ')
consume_funciones(presentar) #Me llamo Luis
consume_funciones(estudiemos_juntos) #Hey Luis aprendamos Python!```

Este c贸digo no corre, no hay ning煤n vinculo entre funcion_entrante(鈥淒avid鈥) y la funci贸n presentarse() o estudiemos_juntos()

def presentarse(nombre):
    	return f"Me llamo {nombre}"

def estudiemos_juntos(nombre):
	return f"隆Hey {nombre}, aprendamos Python!"

def consume_funciones(funcion_entrante):
	return funcion_entrante("David")

consume_funciones(presentarse)
consume_funciones(estudiemos_juntos)```

No se si soy solo yo pero siento que el articulo no le da la verdadera importancia a los decoradores, no se muestra un ejemplo de verdad de como es que se modifica el comportamiento de una funci贸n con un decorador, dejo yo este peque帽o codigo

def my_decorator(fun):
    def wrapper(*args, **kwargs):
        print("antes")
        result = fun(*args, **kwargs)
        return 1 + result
    return wrapper

@my_decorator
def suma(a, b):
    return a + b


if __name__ == '__main__':
    print(suma(1,2))

Tal vez no es mucho pero aqu铆 se ve como si se modifica el comportamiento por default de una funci贸n, es decir el resultado por si mismo de la funci贸n deber铆a ser 4 pero despu茅s de decorar la funci贸n el resultado es 4, esto por que la funci贸n intermedia wrapper toma el resultado original y le suma un 1 mas.

Espero esto ayude a comprender un poco mejor el verdadero poder de los decoradores, adem谩s de que no solo aplica a funciones si no tambi茅n a clases y tambi茅n podemos pasar par谩metros a los decoradores, aqui dejo un articulo que muestra un poco esto https://medium.com/@LuisMBaezCo/decoradores-con-clases-y-funciones-en-python-2fafb22dba43

toca estudiar un poco mas este tema

En Java recuerdo que creabamos un archivo aparte con todas las funciones uqe utilizar铆a nuestro programa y nadamas las ibamos invocando cuando las utilizabamos., tambien recuerdoque pod铆amos escribirle que significa cada parametro como una ayuda al usuario. Espero que m谩s adelante veamos eso. 馃槃