Decorador de python? – ¿Puede alguien por favor explicar esto?

Disculpas, esta es una pregunta muy amplia.

El siguiente código es un fragmento de algo que se encuentra en la web. La clave que me interesa es la línea que comienza con @protegida. Me pregunto qué hace esto y cómo lo hace. Parece estar verificando que un usuario válido haya iniciado sesión antes de ejecutar la función do_upload_ajax. Eso parece una manera realmente efectiva de hacer la autenticación de usuario. Sin embargo, no entiendo la mecánica de esta función @. ¿Puede alguien guiarme en la dirección correcta para explicar cómo se implementaría esto en el mundo real? Python 3 respuestas por favor. Gracias.

@bottle.route('/ajaxupload', method='POST') @protected(check_valid_user) def do_upload_ajax(): data = bottle.request.files.get('data') if data.file: size = 0 

Echa un vistazo a esta enorme respuesta / novela . Es una de las mejores explicaciones que he encontrado.

La explicación más breve que puedo dar es que los decoradores envuelven tu función en otra función que devuelve una función.

Este código, por ejemplo:

 @decorate def foo(a): print a 

sería equivalente a este código si elimina la syntax del decorador:

 def bar(a): print a foo = decorate(bar) 

Los decoradores a veces toman parámetros, que se pasan a las funciones generadas dinámicamente para alterar su salida.

Otro término que debe leer es cierre , ya que ese es el concepto que permite a los decoradores trabajar.

La syntax del decorador:

 @protected(check_valid_user) def do_upload_ajax(): "..." 

es equivalente a

 def do_upload_ajax(): "..." do_upload_ajax = protected(check_valid_user)(do_upload_ajax) 

Pero sin la necesidad de repetir el mismo nombre tres veces. No hay nada más que eso.

Por ejemplo, aquí hay una posible implementación de protected() :

 import functools def protected(check): def decorator(func): # it is called with a function to be decorated @functools.wraps(func) # preserve original name, docstring, etc def wrapper(*args, **kwargs): check(bottle.request) # raise an exception if the check fails return func(*args, **kwargs) # call the original function return wrapper # this will be assigned to the decorated name return decorator 

Un decorador es una función que toma una función como único parámetro y devuelve una función. Esto es útil para “envolver” la funcionalidad con el mismo código una y otra vez.

Utilizamos @func_name para especificar un decorador que se aplicará en otra función.

El siguiente ejemplo agrega un mensaje de bienvenida a la cadena devuelta por fun (). Toma fun () como parámetro y devuelve welcome ().

 def decorate_message(fun): # Nested function def addWelcome(site_name): return "Welcome to " + fun(site_name) # Decorator returns a function return addWelcome @decorate_message def site(site_name): return site_name; print site("StackOverflow") Out[0]: "Welcome to StackOverflow" 

Los decoradores también pueden ser útiles para adjuntar datos (o agregar atributos) a las funciones.

Una función decoradora para adjuntar datos a func.

 def attach_data(func): func.data = 3 return func @attach_data def add (x, y): return x + y print(add(2, 3)) print(add.data) 

Un decorador es una función que toma otra función y extiende el comportamiento de la última función sin modificarla explícitamente. Python permite funciones “anidadas”, es decir (una función dentro de otra función). Python también te permite devolver funciones de otras funciones.

Digamos que su función original fue llamada orig_func ().

 def orig_func(): #definition print("Wheee!") orig_func() #calling 

Ejecuta este archivo, el orig_func () se llama y se imprime. “wheee”.

Ahora, digamos, queremos modificar esta función, hacer algo antes de llamar a esta función y también algo después de esta función.

Entonces, podemos hacer esto, ya sea por la opción 1 o por la opción 2

——–Opción 1———-

 def orig_func(): print("Wheee!") print "do something before" orig_func() print "do something after" 

Tenga en cuenta que no hemos modificado el orig_func. En cambio, hemos realizado cambios fuera de esta función. Pero puede ser que queramos hacer cambios de tal manera que cuando se llame a orig_func, podamos hacer algo antes y después de llamar a la función. Entonces, esto es lo que hacemos.

——–opcion 2———-

 def orig_func(): print "do something before" print("Whee!") print "do something after" orig_func() 

Hemos logrado nuestro propósito. ¿Pero a qué precio? Tuvimos que modificar el código de orig_func. Esto no siempre es posible, especialmente, cuando alguien más ha escrito la función. Sin embargo, queremos que cuando se llame a esta función, se modifique de tal manera, que se pueda hacer algo antes y / o después. Entonces el decorador nos ayuda a hacer esto, sin modificar el código de orig_func. Creamos un decorador y podemos mantener el mismo nombre que antes. Entonces, si nuestra función es llamada, se modifica de manera transparente. Pasamos por los siguientes pasos. a. Definir el decorador. En el docorador, 1) escriba el código para hacer algo antes de orig_func, si lo desea. 2) llamar al orig_func, para hacer su trabajo. 3) escribe el código para hacer algo después de orig_func, si quieres. segundo. Crea el decorador c. Llama al decorador.

Así es como lo hacemos.

================================================== ===========

 #-------- orig_func already given ---------- def orig_func(): print("Wheee!") #------ write decorator ------------ def my_decorator(some_function): def my_wrapper(): print "do something before" #do something before, if you want to some_function() print "do something after" #do something after, if you want to return my_wrapper #------ create decorator and call orig func -------- orig_func = my_decorator(orig_func) #create decorator, modify functioning orig_func() #call modified orig_func 

================================================== =============

Tenga en cuenta que ahora se ha modificado orig_func a través del decorador. Entonces, cuando llame a orig_func (), ejecutará my_wrapper, lo que hará tres pasos, como ya se ha descrito.

Por lo tanto, ha modificado el funcionamiento de orig_func, sin modificar el código de orig_func, ese es el propósito del decorador.

Primero, necesitamos entender por qué necesitamos a Decorator.

Necesidad de decorador: Queremos utilizar la función de la biblioteca ya incorporada de Python que puede realizar nuestra tarea deseada.

Problemas: Pero el problema es que no queremos una salida de función exacta. Queremos salida personalizada. El truco es que no podemos cambiar el código original de la función. Aquí el decorador viene en nuestra ayuda.

Soluciones: Decorator toma nuestra función requerida como entrada, la envuelve en una función de envoltura y hace 3 cosas:

  1. hacer algo antes
  2. luego llame a la función deseada ().
  3. hacer algo despues

Código general:

 def my_decorator(desired_function): def my_wrapper(): print "do something before" #do something before, if you want to desired_function() print "do something after" #do something after, if you want to return my_wrapper desired_func = my_decorator(desired_func()) #create decorator desired_func() #calling desired_func() 

Como a algunas personas les gusta aprender en formato de video, esta es la mejor explicación que he visto de los decoradores de Python. En este video (haga clic en el enlace para ir al inicio del tema) James Powell lo lleva a través de toda la historia de los decoradores para que tenga una idea muy clara de por qué y cómo.

https://youtu.be/cKPlPJyQrt4?t=3099