Aún no tienes acceso a esta clase

Crea una cuenta y continúa viendo este curso

Curso Profesional de Python

Curso Profesional de Python

Facundo García Martoni

Facundo García Martoni

Decoradores

10/21
Recursos

Aportes 79

Preguntas 8

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad? Crea una cuenta o inicia sesión.

En 6 minutos Facundo explica lo que otros se gastan un curso entero y no logran hacer entender.
De lejos el mejor docente de Platzi.

Esta clase es brutalmente buena, muchas veces intente entenderlo, pero hoy todo queda más claro, la clave fue explicar el scope, closure y decoradores!

Para entender un poco este tema me remití al curso de Python del canal Píldoras informáticas en YouTube. Clic aquí para ver el vídeo.
.
La sintaxis sería la siguiente:

def <funcion_decorador> (<funcion>):

    def <funcion_interna> ():

        <código de la funcion_interna>

    return <funcion_interna>

No obstante, dejó el código de ejemplo que usó para explicar este tema por si a alguien le interesa:

def funcion_decoradora(funcion_parametro):

    def funcion_interior():

        #Acciones adicionales que decoran

        print("Vamos a realizar un cálculo: ")

        funcion_parametro()

        #Acciones adicionales que decoran

        print("Hemos terminado el cálculo")

    return funcion_interior


# Para llamar la función decoradora se usa el carácter "@" y a continuación el nombre de esta.
# Python entiende que la función adyacente hacia abajo es la función a decorar y la toma como parametro.

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

suma()

El resultado en consola sería el siguiente:

>>> Vamos a realizar un cálculo: 
>>> 35
>>> Hemos terminado el cálculo

“Si estas leyendo esto, significa que estas a la mitad del camino. ¡No te rindas! Ya solo falta la mitad 😃”

Lei esto en otro curso. Espero que te ayude tanto como lo hizo conmigo 🚀
👾

Decoradores

Un decorador es una función que recibe como parámetro otra función, le añade cosas y retorna una función diferente. Tienen la misma estructura que los Closures pero en vez de variables lo que se envía es una función. Ejemplo:

def decorador(func):
    def envoltura():
        print("Esto se añade a mi función original.")
        func()
    return envoltura

def saludo():
    print("¡Hola!")

saludo()
# Salida:
# ¡Hola!

saludo = decorador(saludo) # Se guarda la función decorada en la variable saludo
saludo()                   # La función saludo está ahora decorada
# Salida:
# Esto se añade a mi función original.
# ¡Hola!

Se puede hacer de manera mas sencilla, con azúcar sintáctica (sugar syntax): Cuando tenemos un código que está embellecido para que nosotros lo veamos de una manera más estática, ayudando a entender de manera mas sencilla el código. De esta manera, tenemos el código anterior:

def decorador(func):
    def envoltura():
        print("Esto se añade a mi función original.")
        func()
    return envoltura

def saludo():
    print("¡Hola!")
saludo = decorador(saludo) # Se guarda la función decorada en la variable saludo (se decora)

saludo()                   # La función saludo está ahora decorada 
def decorador(func):
    def envoltura():
        print("Esto se añade a mi función original.")
        func()
    return envoltura

# De esta manera se decora la función saludo (equivale a saludo = decorador(saludo) de la última línea, quedando ahora en la línea inmediata superior ):
@decorador                
def saludo():
    print("¡Hola!")

saludo()                   # La función saludo está ahora decorada 

Esto permite ahorrar código al implementar características (decoradores) comunes a diferentes funciones:

def decorator_upper(func):                  # Función decoradora
    def wrapper(text):                      # Función anidada
        return func(text).upper()           # Operación que realiza el decorado a la función (func), inserta el texto a la función original. Convierte todo a mayúsculas.
    return wrapper                          # Devuelve wapper como indica la regla de los Clousures

@decorator_upper                            # Decora la función message
def message(name):
    return f'{name}, recibiste un mensaje'  # Esto es lo que realiza la función message, previo a ser decorada.

@decorator_upper                            # Decora la función warning
def warning(name):
    return f'Usa solo mayúsculas {name}'  # Esto es lo que realiza la función warning, previo a ser decorada.

print(message("Cesar")) # Output: CESAR, RECIBISTE UN MENSAJE
print(warning("Cesar")) # Output: USA SOLO MAYÚSCULAS CESAR

Así lo pude entender mejor, espero les sirva.

Ahora si entiendo el uso de los clouser. La funcion tiene que llamarse en la nested para que la funcion decorador pueda incluirla. Sin la nested tendriamos que llamar a la funcion decorador e incluirle la funcion hola. Con este sistema solo creamos el decorador con nested y lo colocamos como decorador en la funcion que queremos decorar. Ahora si lo entendi. Thanks

AZUCAR SINTACTICA: sintaxis anadida con el unico proposito de embellecer un codigo para hacerlo mas PYTHONICO

sigueme para mas reviews de frases epicas de estos cursos

Eso de azucar sintáctica me recodó a Celia Cruz.

Un concepto que no había terminado de entender, pero ahora todo me queda claro 😁

'''
   Decorador: Es una funcion que recibe como parametro otra funcion, le añade cosas y retorna una funcion diferente.
   -> Una funcion que le añade super poderes a otra funcion.
'''

def decorador(func):
    def envoltura():
        print('Esto se añade a mi funcion original')
        func()
    return envoltura

def mayusculas(func):
    def envoltura(texto):
        return func(texto).upper()
    return envoltura


@decorador
def saludo():
    print('Hola')

@mayusculas
def mensaje(nombre):
    return f'{nombre}, recibistes un mensaje'

saludo()
print(mensaje('duvan'))

Aqui un video que explica muy bien como se aplican y para que sirven los decoradores en Programacion Orientada a Objetos en Python:
https://www.youtube.com/watch?v=jCzT9XFZ5bw&list=LL&index=100&t=95s&ab_channel=CoreySchafer

Lo que sufrí en su momento para entender los Decoradores, sin entenderlo en ese momento, para que luego en 3 videos, entienda a la perfección. Este curso solo por esto ya ha valido totalmente la pena

Facundo lo explico super sencillo
.
Pero te cuento que este concepto es mucho más grande, decorador es un patron de diseño
.
El patrón Decorator responde a la necesidad de añadir dinámicamente funcionalidad a un Objeto. esto tiene que ver mucho con programación orientada a objetos
.
Al utilizar este patrón, se pueden añadir y eliminar funcionalidades en tiempo de ejecución
.
Si deseas leer más ve aquí

Si alguien quiere alguna vez copiar un texto o en este caso codigo de una imagen o video puede usar la extension de google BlackBox, solo seleccionan la zona como si fuera una screenshot y ya esta copiado :d

Decoradores

  • Es el concepto más avanzado de funciones 😵. Un decorador es un closure especial, con una función adicional.

  • Un decorador es una función que recibe como parámetro otra función, le añade cosas, y retorna una función diferente. Le da superpoderes a una función 🦸‍♀️.

    def decorador(func):
    	def envoltura():
    		print('Esto se añade a mi función original')
    		return envoltura
    
    def saludo():
    	print('Hola!')
    
    saludo() # Hola!
    saludo = decorador(saludo)
    saludo() # Esto se añade a mi función original. Hola!
    
  • Esto, a ser un patrón muy común, hay una manera mas pythonica de hacerlo, con azúcar sintáctica. Esto es cuando tenemos un código que está embellecido para que sea más fácil de entender. (Sugar sintax). Sería algo así:

    def decorador(func):
    	def envoltura():
    		print('Esto se añade a mi función original')
    		return envoltura
    
    @decorador
    def saludo():
    	print('Hola!')
    
    saludo() # Esto se añade a mi función original. Hola!
    
  • Otro ejemplo:

    def mayusculas(func):
    	def envoltura(texto):
    		return func(texto).upper()
    	return envoltura
    
    @mayusculas
    def mensaje(nombre):
    	return f'{nombre} recibiste un mensaje'
    
    print(mensaje('Cesar'))
    

🙌 Pause el video y resolví por mi cuenta

# Creación del decorador
def decorator(greeting):
    def wrapper():
        print('This is in addition to my greeting function.')
        greeting()
    return wrapper


def greeting():
    print('Hi!')


greeting()  # Hi!

saludo = decorator(greeting)
saludo()
# !This is in addition to my greeting function.!
# Hi!



def upper(message):
    def wrapper(text: str):
        assert type(text) == str, 'You must enter a string in this funtion'
        return message(text).upper()
    return wrapper
        

@upper
def message(name: str) -> str:
    return f'{name}, you get a new message!'


def run():
    print(message('cesar'))


if __name__ == '__main__':
    run()

En general, un decorador es un patrón de software que se utiliza para alterar el funcionamiento de una determinada pieza de código; ya sea una función, o una clase, sin la necesidad de emplear otros mecanismos como la herencia.

En concreto, al hablar de los decoradores en Python, nos referimos a funciones u objetos con un comportamiento similar que nos permiten alterar cómo funcionan otras entidades sin tener que modificar su código explícitamente.

Para entender un poco mejor la idea, es necesario aclarar unas cuantas cosas acerca de las funciones. Las funciones son, para fines prácticos, objetos, y como tales pueden ser pasadas como parámetros, reasignadas, devueltas por otras funciones e incluso definidas dentro de funciones, es decir, pueden ser anidadas.

Toma el siguiente código como ejemplo:

def pares(numeros):
    def es_par(numero):
        return (numero % 2) == 0
    return list(filter(es_par, numeros))

Aquí se utiliza la función anidada es_par() para filtrar los números pares en una lista de valores arbitraria.

En el caso de los decoradores, las funciones anidadas nos sirven para desempeñar tareas alrededor de la ejecución de la rutina que deseamos decorar. Para ilustrar la situación te mostraré un decorador que toma un texto que consiste de varios renglones y a cada uno de estos le agrega como prefijo el número de línea que le corresponde. Empecemos con una función ordinaria que realiza esta tarea:

def agrega_numero_de_linea(texto):
    lineas = []
    for numero, linea in enumerate(texto.split('\n'), 1):
        lineas.append(f'{numero:6} {linea}')
    return '\n'.join(lineas)

De este modo, si quisiéramos tener líneas numeradas en un texto, basta con llamar esta función:

def diccionario_a_texto(diccionario):
    texto = ''
    for llave, valor in diccionario.items():
        texto += f'{llave:12}: {valor}\n'
    return texto
juan = {
    'nombre': 'Juan Pérez',
    'edad': 25
}
print(diccionario_a_texto(juan))

nombre      : Juan Pérez
edad        : 25

print(agrega_numero_de_linea(diccionario_a_texto(juan)))

     1 nombre      : Juan Pérez
     2 edad        : 25
     3

Sin embargo, esto no se ve muy elegante, además de que reutilizar la función agrega_numero_de_linea() no resulta tan sencillo. Un decorador nos ayudará a ocultar el código que numera la línea y a aprovecharla en otras funciones de una manera fácil y elegante:

def con_linea_numerada(funcion):
    def agrega_numero_de_linea(*args, **kwargs):
        resultado = funcion(*args, **kwargs)
        lineas = []
        for numero, linea in enumerate(str(resultado).split('\n'), 1):
            lineas.append(f'{numero:6} {linea}')
        return '\n'.join(lineas)
    return agrega_numero_de_linea

La función de arriba es un decorador, la cual recibe como argumento otra definida en el exterior, y devuelve una tercera rutina definida internamente, una función anidada. La parte interesante es que la función anidada invoca a la externa y posteriormente procesa dicho resultado, agregando la funcionalidad del decorador al resultado final. Una vez definido el decorador, podemos utilizarlo de la siguiente manera:

def diccionario_a_texto(diccionario):
    texto = ''
    for llave, valor in diccionario.items():
        texto += f'{llave:12}: {valor}\n'
    return texto
diccionario_a_texto = con_linea_numerada(diccionario_a_texto)
juan = {
    'nombre': 'Juan Pérez',
    'edad': 25
}
print(diccionario_a_texto(juan))

     1 nombre      : Juan Pérez
     2 edad        : 25
     3

Nota, como inmediatamente después de definir la función diccionario_a_texto, esta es redefinida utilizando el decorador. Esta instrucción es tan común al utilizar decoradores, que Python ha definido un poco de azúcar sintáctica para representarla: simplemente hay que colocar el nombre del decorador, con un arroba como prefijo, antes de la definición de la función que quieres decorar, en nuestro caso, la definición se verá así:

@con_linea_numerada
def diccionario_a_texto(diccionario):
    texto = ''
    for llave, valor in diccionario.items():
        texto += f'{llave:12}: {valor}\n'
    return texto
juan = {
    'nombre': 'Juan Pérez',
    'edad': 25
}
print(diccionario_a_texto(juan))

     1 nombre      : Juan Pérez
     2 edad        : 25
     3

Esto hace que reutilizar el decorador sea todavía más sencillo:

@con_linea_numerada
def muestra_archivo(nombre_de_archivo):
    with open(nombre_de_archivo) as archivo:
        return archivo.read()

Como puedes darte cuenta, los decoradores son muy útiles para reutilizar código que desempeña tareas comunes, las posibilidades son muy variadas, desde dar formato a texto, como en el ejemplo que te he mostrado, sincronizar tareas en paralelo, o manejar errores. El límite es nuestra creatividad.

Apuntes de la clase
Decoradores

Es un closure especial, es una función que recibe parametros de otra función, le añade cosas y retorna una función diferente

def decorador(func):
    def envoltura():
        print('esto se añade a mi función original')
        func()
    return envoltura

def saludo():
    print('¡Hola!')
    
#llamado sencillo
saludo()

#llamado decorado
saludo = decorador(saludo)
saludo()

#output
¡Hola!
esto se añade a mi función original
¡Hola!

Una forma más pythonica es con ‘Azúcar Sintáctica’/Sugar Syntax, ‘enbellece’ el código para comprenderlo mejor

def decorador(func):
    def envoltura():
        print('esto se añade a mi función original')
        func()
    return envoltura

@decorador
def saludo():
    print('¡Hola!')
    
saludo()

#output
esto se añade a mi función original
¡Hola!

Otro ejemplo:

def mayusculas(func):
def envoltura(texto):
return func(texto).upper()
return envoltura

@mayusculas
def mensaje(nombre):
return f'{nombre}, recibiste un mensaje'

print(mensaje('Felipe'))

#output
FELIPE, RECIBISTE UN MENSAJE

sirve para poder darle un tratamiento especifico a una función que ya se tiene ejecutando

Los Decoradores son un closure especial por que además de ser un clousure también modifica la función original.
Recibe como parámetro a una función, añade algo y retorna otra función.
Con Sugar syntactic @decorador

Esta es la explicación que yo le doy a los decoradores:
La funcion decoradora añade funcionalidad a mi funcion, para ello debemos pasar nuestra funciona a la funcion decoradora y lo hacemos por parametro.
La funcion decoradora contine una funcion interna (envolvente o wrapper) que es la que realmente añade la funcionalida a mi funcion. La funcion decoradora retorna al exterior (scope superior) a esta funcion interna para poder ser usada al mismo nivel que nuestra funcion.

La explicacion en codigo seria:

  def funcion_decoradora(funcion_parametro):
    def funcion_interior():
      # codigo pre decorado
      # ...
      funcion_parametro()
      # codigo post decorado
      # ...

    return funcion_interior

  # definicion de mi funcion
  def mi_funcion():
      # codigo de mi funcion
      Print('Hola')

  # envoltura de mi funcion
  mi_funcion = funcion_decoradora(mi_funcion)
  
  # llamada a mi funcion decorada
  mi_funcion() 

Esta manera de hacer (patron de programacion) se puede escribir en codigo usando @ de esta manera:

def funcion_decoradora(funcion_parametro):

    # definicion de la funcion envolvente
    def funcion_interior():
      
      # codigo pre decorado
      # ...
      funcion_parametro()
      # codigo post decorado
      # ...

    return funcion_interior

  # definicion y envoltura todo al mismo tiempo
  @funcion_decoradora
  def mi_funcion():
      # codigo de mi funcion
      Print('Hola')

  # llamada a mi funcion ya decorada
  mi_funcion()

Y ahora una cuestion para mis compañeros de clase:
Al usar el decorador (al estilo @) ¿Dejo de poder usar mi funcion SIN decorar?
Porque sin usar @ yo podria renombrar mi funcion decorada y tener dos funciones distintas, pero usando @ se sobreescribe mi funcion pasando a estar decorada de ahora en adelante.

mi_funcion_decorada = funcion_decorador(mi_funcion)
mi_funcion_decorada()
mi_funcion()

Bueno ya vale de rollo. Decidme si estoy equivocado. Gracias

Mi aporte con un plato peruano.

def guarnicion(func):

    def acompanar():
        print ('camote, cancha y zarandaja')
        func()
    return acompanar

@guarnicion
def ceviche():
    fs = 'pescado'
    ln = 'limón'
    cpp= 'ají'
    print(fs+" "+ln+" "+cpp)

#ceviche = guarnicion(ceviche)

def run():

    ceviche()

if __name__ == '__main__':
    run()

excelente claseeeeee brutaaal

Muy claro y bien explicado, debes de tener bien todas la bases de programación y conocer bien la aplicación de la sintaxis de Python, todo saldrá super

Sigo diciéndolo Facundo es el profesor en Platzi

Syntactic sugar

Si quieren ver los paso a paso que va realizando el codigo les recomiendo memlayout

👇🏼👇🏼👇🏼👇🏼

Super de verdad que no me esperaba ver esto en este curso

que explicación tan buena, concreta y sencilla

Excelente explicación, pero…, pero el buen Facundo no uso el inglés en la codificación como lo planteó en el segundo curso, con lo cual hubiera quedado exquisito el código, saludos

Este es el tercer curso en el que veo decoradores y es el primero en el que le entiendo

Esta clase hace todo el curso, y habla muy bien de Platzi, de esas ganas de mejorar siempre. Anteriormente habia un curso que tomaba casi 4 clases para explicar decoradores y no era tan claro el concepto al final de esas clases. Bien por Platzi y chapeu por Facundo.

vi 3 veces el video pero vi en los comentarios que en la clase siguiente se resuelven varias dudas

Es verdad que son preguntas de entrevista de trabajo, cabo de tener una y este video me salvo.

Que genial la explicación de decoradores, 👏
Un lujo esta clase!

Y que pasa cuando el decorador necesita parametros?
Este fue el problema con el que alguna vez me habia enfrentado. He aqui mi solucion para un algoritmo de backoff exponencial para reintentos de una peticion.

import random, time

class SentToAPI():
    def _retry_with_backoff(_send_to_api, max_attempts:int=5, seconds:int=1):
        def wrap(self, *args, **kwargs):
            attempt = 0 
            while True:
                try:
                    return _send_to_api(self)
                except:
                    if attempt == max_attempts:
                        print("Closing Connection...")                        
                        raise TimeoutError("TimeoutError: Number of attempts exceeded...")
                    else:
                        exponetial_time = (seconds * (2 ** attempt) + random.uniform(0,1))
                        print("Wating {}sec to retry...".format(exponetial_time))
                        time.sleep(exponetial_time)
                        attempt += 1
        return wrap

    @_retry_with_backoff(max_attempts=10, seconds=2)
    def _send_to_api(self):
        
        response, code, reason = get_response()

        if code == 200:
            print("All successful logs were inserted successfully")
        else:
            if code >= 300 and code < 500:
                print("All failed logs were inserted successfully")
            elif code >= 500:
                raise Exception("Server has response with a code 500. Trying to send request again...")

        print(f"Response code: {code}, {reason}")
        
        print("Closing Connection...")
        try:
            response.close()
            print("Connection Closed...")
        except Exception as ex:
            print(f"There was a problem closing the connection: \n\t{ex}")

Para mi seria importante para quienes se inician mencionar que decorador es un patron de diseño. Los decoradores se implementan en la mayoria de los lenguajes modernos y Python tiene una implementacion especial pero el concepto es mas amplio.

En el minuto 3:40 se habla de azúcar sintáctica, solo un aporte para no confundirlo con una abstraction lingüística, ambas son parecidas, pero el azúcar tiene el objetivo principal de reducir código, no crear una abstracción, un ejemplo de una abstraccion en algunos lenguajes es el operador for o while que son abstracciones de procesos de iteracion.

Crack! increíble lo simple que lo hace ver despues de haber explicado closures! Gracias por tanto profe!

Entonces puedo decir que un decorador es una high order function ya que recibe otra funcion como parametro y asi mismo tambien es un closure ya que devuelve una function interna que en un su proceso hace uso del contexto de su funcion superior, y en conjunto logran extender la funcionalidad de la funcion decorada

Siento que con esta clase ya se pago el año de Platzi.

I think the correct way to say “azucar sintactica” is syntactic sugar instead of sugar syntax.

You can check this directly on Wikipedia.

Facundo acabas de explicar en 6:19min lo que ningún otro vídeo, foro o tutor ha logrado hacerme entender, Infinitas gracias. ♥

Se me explotó el cerebro con esta clase. Increible cómo en 6 minutos se entiende tanto. Brutal!! 👏

DECORADOR
Función que recibe como parámetro otra función, le añade cosas, y retorna una función diferente. Añade cosas a la función original.
El decorador se identifica con un @ y el nombre de la función.

Dentro del Decorador nos encontramos con una nested function/funcion anidada que se le suele poner el nombre de wrapper/envoltura

def upper_text(func):
    def wrapper(text):
        return func(text).upper()
    return wrapper

@upper_text
def message(name):
    return f'{name}, you have received a message'

def run():

    print(message('Nestor'))

if __name__ == '__main__':
    run()

# It prints:
# NESTOR, YOU HAVE RECEIVED A MESSAGE

Primera vez que escucho de este tema y pues la verdad siento que quedo muy bien explicado en esta clase muy buen profesor Facundo

primera vez en mi vida que entiendo completamente que es un decorador.

🤯🤯🤯🤯

Un tema tan complejo, fue explicado para comprenderlo de manera precisa; sin afán, sin textos largos que sólo complican todo y yendo paso a paso. Facundo logra lo que nadie más ha logrado en Platzi explicando Python. El mejor

  • Un decorador es una función que recibe como parámetro otra función, le añade cosas y retorna una función diferente.
def decorator(func):
    def envol(args):
       # do new somethings
       func(args)
    return envol

#sugar sintax
@decorator
def myfunc(args):
    #do something
    pass

Si entendí los closures de una forma bastante “fácil”, tomar practicar más, con los decoradores te has salido, y falta lo mejor, seguir practicando para interiorizar más el concepto.

Te has marcado una gran clase, corta, directa, sencilla y totalmente entendible, y eso que cuando se mezcla funciones que retornan funciones y otras cuestiones de esta índole, se embarra el tema, pero bastante entendible.

Gracias Facundo, y también gracias a los compañeros por siempre participar y compartir.

Estaría genial que también se explicará los setters y getters de las classes en python por esta parte del curso. Creo que quedaría genial ya que utilizan decoradores para su funcionamiento.

Es bellisimo!

def mayusculas(func):
    def envoltura(texto):
        return func(texto).upper()
    return envoltura

@mayusculas
def mensaje(nombre):
    return f'{nombre}, recibiste un mensaje' 


print(mensaje('Cesar'))

👾

En otras palabras los decoradores hacen posible que una función o clase (A) tome como argumento otra función (B) a fin de de volver una tercera función ©. Todo esto sin necesidad de cambiar el código fuente.

La utilidad de estos decoradores es hacer un código más limpio y autocompletarlo.

Ya estas en la mitad el curso

def mayuscula(func):
    def envoltura(texto):
        return func(texto).upper()
    return envoltura

@mayuscula
def mensaje(nombre):
    return f'{nombre}, recibiste un mensaje.'
print(mensaje('Cesar'))
Vamos a realizar un cálculo: 
35
Hemos terminado el cálculo

⭐️⭐️⭐️
DECORADOR ES: UNA FUNCIÓN QUE RECIBE COMO PARÁMETRO OTRA FUNCIÓN, LE AÑADE COSAS, Y RETORNA UNA FUNCIÓN DIFERENTE.
⭐️⭐️⭐️

Entendido y para entender más utiliza el debug.

def mayusculas(func):
    def envoltura(texto):
        return func(texto).upper()
    return envoltura

@mayusculas
def mensaje(nombre):
    return f'{nombre}, aprendiendo.'

def run():
    print(mensaje('david'))

if __name__=='__main__':
    run()

Hice un closure que crea un decorador. Dicho decorador retrasa la ejecución de la función que decora. El programa te pide el tiempo que quieres retrasar la función.

A mi me sirvió como reto para entender los conceptos y como podría aplicarlos.

Mi solución:

import time

def delay_by(n):
    def decorator(func):
        def wrapper(*args):
            print(f"Retrasando {n} segundos ...")
            time.sleep(n)
            func(*args)
        return wrapper
    return decorator

def run():
    delay = int(input("Digita una cantidad de segundos: "))
    name = input("Ahora dime tu nombre: ")
    delayer = delay_by(delay)

    @delayer
    def saludar(name):
        print(f"Hola {name}")

    @delayer
    def saludar_3_veces(name):
        print(f"Hola {name}. "*3)

    saludar(name)
    saludar_3_veces(name)

if __name__ == '__main__':
    run() 

No me la contés! En otros lenguajes usaba decoradores porque era lo que decía la teoría y ahora entiendo la lógica que siguen!!! Es un grande el profe Facundo!!!

Hola! no se si les sirva igual que a mi, pero hice este garabato encima del ejercicio para entender como iba pasando el texto en cada fase del codigo. Esta muy bien explicado en la clase pero este dibujo me ayudo a concretar de donde y como llegaba el texto al decorador

![](

Este curso es extremadamente brutal!

los decoradores nos ayudan a hacer mas legible nuestro codigo, mas pythonico

los decoradores ayudan a extender una función, es una funcion que recibe como parametro una funcion y retorna una funcion diferente, los decoradores agregan nuevos metodos, y acciones a nuestra funcion

Facundo es un Crack, Mejor que Maradona !

Este link te lleva a un artćulo del portal Real Python en el que puedes ver mucho más sobre decoradores, es un portal de Python muy interesante con decenas de tutoriales con los que podemos aprender.

Función que recibe como parametro otra función, le añade nuevas funcionalidades y retorna una función diferente.

Una función que le añade super poderes a otra función.

Ejemplo:

def decorador(func):
	def envoltura():
		print("Esto se añade a mi función original")
		func()
	return envoltura

def saludo():
	print("Hola")

saludo() # output: Hola

saludo = decorador(saludo)
saludo = 
# output: 
# Esto se añade a mi función original
# Hola

De forma más estetica:

def decorador(func):
	def envoltura():
		print("Esto se añade a mi función original")
		func()
	return envoltura

@decorador
def saludo()
	print("Hola")

saludo()
# output: 
# Esto se añade a mi función original
# Hola

Acá está el código:

def mayusculas(func):
    def wrapper(texto):
        return func(texto).upper()
    return wrapper

@mayusculas
def mensaje(nombre):
    return f'{nombre}, recibiste un mensaje'

print(mensaje('Cesar'))

Eres un crack Facundo!! Explicas tan bien el concepto que ya no me queda duda alguna de cómo leer e implementar los decoradores. Excelente metodología de enseñanza.

La manera más sencilla de explicar decoradores, maravilloso.
Códigos de las slides:

def decorador(func):
  def envoltura(): #wrapper
    print("Esto se añade a mi función original")
    func()
  return envoltura


@decorador
def saludo():
  print("Hola")


saludo() # Esto se añade a mi función original - Hola
def mayusculas(func):
  def envoltura(texto):
    return func(texto).upper()
  return envoltura


@mayusculas
def mensaje(nombre):
  return f'{nombre}, recibiste un mensaje'


print(mensaje('Cesar'))

Decorators

This is the most advanced concept of a function. A decorator looks like closure with additional specification.

  • Decorator: It’s a function that gets other function as imput parameter, then it adds new things, and finally this returns a different function that the original function.
def decorator(func):
    def wrapper():
        print("it's added to original function")
        func()
    return wrapper

def greet():
    print("hello")

greet()
# Output 
# hello

greet = decorator(greet)
greet()
# Output
# It's added to your origianl function
# hello

Facundo introduces a new concept, syntactic sugar. It’s for make a code more friendly.

def decorator(func):
    def wrapper():
        print("it's added to original function")
        func()
    return wrapper

@decorator
def greet():
    print("hello")


greet()
# Output
# It's added to your origianl function
# hello

here @decorator is the key to use orginal function decorator in greet.

  1. greet gets a function decorator.
  2. it adds new information, print(“hello”)
  3. it returns other function, different to decorator.

gracias profesor siga asi con su metodologia para darnos clase

Función que recibe como parámetro otra función, le añade cosas, y retorna una función diferente.

```python
def decorador(func):
    def envoltura():
        print('Esto se añade a mi función original')
        func()
    return envoltura

def saludo():
    print('Hola!')

saludo()
>> Hola!

saludo = decorador(saludo)
saludo()
>> Esto se añade a mi función original
>> Hola!

De este modo transformamos este pedazo de código en un decorador

def decorador(func):
    def envoltura():
        print('Esto se añade a mi función original')
        func()
    return envoltura

@decorador
def saludo():
    print('Hola!')

saludo()
def mayusculas(func):
        def envoltura(texto):
            return func(texto).capitalize()
        return envoltura

    @mayusculas
    def mensaje(nombre):
        return f'{nombre}, recibiste un mensaje'

    print(mensaje('Alexander'))

Y tambien podemos usar varios decoradores 😮 No sabia hasta que lo experimente jasjas

def decorate_text(func):
    def wrapper(text):
        text_len = len(func(text))
        return "*" * text_len + '\n' + func(text) + '\n' + "*" * text_len

    return wrapper


def uppercase(func):
    def wrapper(text: str) -> str:
        return func(text).upper()

    return wrapper


@decorate_text
@uppercase
def say_hi(name: str) -> str:
    return f'hi {name}'


def run():
    print(say_hi('Run'))


if __name__ == '__main__':
    run()

Dejo un artículo para ampliar información:
https://codigofacilito.com/articulos/decoradores-python