What does a self-recording decorator (e.g. @login_required) actually do?

In my Flask-App, I defined a function of the form as follows:

@app.route("/some/restricted/stuff") @login_required def main(): return render_template("overview.html", stuff = getstuff() ) 

Where the decorator is defined as:

 def login_required(something): @wraps(something) def wrap(*args, **kwargs): if "some_admin_name" in session: return something(*args, **kwargs) else: flash("\"You shall not pass!\" - Gandalf") return redirect(url_for("login")) return wrap 

I just copied this because I found several sources where this code is used, but did not explain.

It's pretty simple to understand WHAT this code does: it allows me to use a decorator that is called after app.route () and before main () for each request, which allows me to do things like checking for active login and the like.

So, as a newbie to Flask / Python, I just wanted to know HOW exactly this works, especially:
- What is the argument "something"? This is a request?!
- What are arg and quargs (key arguments?)?
Should I wrap the method INSIDE the method to use it as a decorator?
β€œCan this be used only with a flask?” Are there any other situations that may come in handy?

+7
python flask login wrap
source share
3 answers

Welcome to Python! This is a lot of questions. Let them take them one at a time. Also, just a fair warning point. This question makes your head spin for a while before everything clicks together.

For reference, here is your decorator and function, decorated with your example:

 # Decorator Function def login_required(something): @wraps(something) def wrap(*args, **kwargs): if "some_admin_name" in session: return something(*args, **kwargs) else: flash("\"You shall not pass!\" - Gandalf") return redirect(url_for("login")) return wrap # Function Being Decorated @app.route("/some/restricted/stuff") @login_required def main(): return render_template("overview.html", stuff = getstuff() ) 

What is the argument for something? Is this a request ?!

To answer this question, we must first answer what a decorator is. The answer may vary slightly depending on what type of object you are decorating. In this case, when you decorate a function, you can think of the decorator as a method / function that allows the programmer to modify the behavior of another function.

With this in mind, we can answer your question. "something" is the function / method you are about to decorate. Yes, this is a function that takes another function as an argument.

Change the language of your decorator function to make it clearer:

 def login_required(function_to_wrap): @wraps(function_to_wrap) def wrap(*args, **kwargs): if "some_admin_name" in session: return function_to_wrap(*args, **kwargs) else: flash("\"You shall not pass!\" - Gandalf") return redirect(url_for("login")) return wrap 

What are args and kwargs?

The short answer is that this is a Python way, allowing parameter programmers to write functions / methods that take a variable number of arguments with and without a keyword.

Usually, when you write a function, you explicitly specify the parameters. For example:

 def add_these_numbers(number_1, number_2): return number_1 + number_2 

This, however, is not the only way to do things. You can also use * args or ** kargs to achieve the same:

 def add_these_numbers(*args): return args[0] + args[1] def add_these_numbers_too(**kwargs): return kwargs['first_number'] + kwargs['second_number'] 

Since it relates to your *args/**kwargs question, it is usually used in decorators, because decorators are often applied to a variety of methods that will take a wide variety of parameters.

Using args/**kwargs , your decorator method can pass which method is initially required for the method through the decorator function. If this makes your head spin, let me know and I'll try to clarify.

Change main () so that it is more clear:

 # Function Being Decorated @app.route("/some/restricted/stuff") @login_required def main(html_template): return render_template(html_template, stuff = getstuff()) 

Why do I need to wrap the INSIDE method to use it as a decorator?

This is the hardest part of understanding decorators, in my opinion. The key understands that at its core the decorator takes on the name of the original function.

The easiest way to understand this is to use the decorator without using the convenient @ syntax. The following equivalents:

 @login_required def main(): .... main = login_required(main) 

Hold on to your horses, this is where it gets AWESOME! . Both of these code snippets tell Python: The "main" word should no longer refer to the main () function, but to the results of the login_required () function when the original main () function was passed as a parameter.

WAT !?

Yes. The main () call now refers to the results of the login_required (main ()) call. That is why login_required returns a nested function. The new main () should still be a function, like the old.

The difference is that now the new main function is really an instance of wrap () configured by the parameter passed to login_required ().

So ... effectively main () is now equivalent to the following:

 def main(*args, **kwargs): if "some_admin_name" in session: return predecorator_main_function(*args, **kwargs) else: flash("\"You shall not pass!\" - Gandalf") return redirect(url_for("login")) 

Is it only used with a jar? Could other situations be helpful?

Definitely not! Decorators are one of many cutting-edge features built right into Python. Decorators are useful in any situation when you want to make changes (relatively small in my opinion) to existing functions / methods, when you do not want to create additional functions to avoid code duplication.

+20
source share

Introduction to decorators: you can read here .

What are the arguments and kwargs (keyword arguments?)?

args , when you do not know how many arguments can be passed to your function.

 random(*args) random('d', 'c', 'b') random('a', 'e', 'f','z') 

kwargs : named arguments. Basically, as above, it looks like you are passing a dictionary.

 random(**kwargs) random(a="a", b='test', c='ab') 

Why do I need to wrap the INSIDE method to use it as a decorator?

Check out the link decorator link.

Can I use only a flask? Are there any other situations? How can this come in handy?

No, decorators are available in Python in general, but are not limited to just a jar. You can use it for registration, synchronization and many other uses. In Django, you can resolve the message or get the request using the decorator or for login_required, etc.

+1
source share

It:

 @deco def foo(): return 100 

It is equal to:

 def temp_foo(): return 100 foo = deco(temp_foo) 

The login_required definition takes the old main as something , wraps a new function around it, and returns it.

0
source share

All Articles