Hello!
Translation of the article was prepared for students of the course "Web-developer in Python . " Is it interesting to develop in this direction? Sign up for the Open Doors Day course and chat live with the teacher: online broadcast July 23 at 20:00 Moscow time!
When you practiced programming in Python, you must have come across a concept like decorators. They are one of the most elegant and frequently used tools in modern libraries and frameworks. Decorators are a good way to encapsulate many implementation details, leaving a simple interface on the surface.
Let's look at an example of a simple login decorator that checks that a user is logged in before giving him the opportunity to edit posts. Then the decorator redirects to the login or registration page, and then with the help of correctly set parameters returns to the same page after successful authentication. To use this function, you just need to write @login_required before the target function.
@login_required def edit_post(post_id): ...
Decorators are very easy to work with, but creating decorators is not an easy task even for experienced Python programmers. In this article, we will take a step-by-step look at how decorators work in Python.
Functions are also called first class objects in Python. Functions are the same values as numbers, lists, and strings, as seen in the following example.
>>> def foo(): ... return 1 ... >>> >>> foo() 1 >>>
Functions have their own namespace, where they search for variable names in the first place when they occur in the body of a function. Let's write a simple function to understand the difference between global and local scope.
>>> >>> a_string = "This is a global variable" >>> >>> def foo(): ... print(locals()) ... >>> >>> print(globals()) {..., 'a_string': 'This is a global variable'} >>> >>> foo() # 2 {} >>>
The scope rule in Python says that when a variable is created, a new local variable is always created, but access to the variable is determined in the local scope when it searches all nearby areas for matching variable names. This does not mean that we cannot access global variables from our function. To output a global variable, we change the function foo as follows:
>>> >>> a_string = "This is a global variable" >>> >>> def foo(): ... print(a_string) #1 ... >>> >>> foo() This is a global variable >>>
Not only variables live in the namespace, but they also have a lifetime, which is important to remember. Consider an example that illustrates not only scope rules and the problems that they can cause, but also how they interact with function calls and how they work in Python and other languages.
>>> def foo(): ... x = 1 ... >>> foo() >>> >>> print(x) # 1 Traceback (most recent call last): ... NameError: name 'x' is not defined >>>
You can create nested functions in Python, which means that we can declare functions inside functions, and all scope and lifetime rules still apply.
>>> >>> def outer(): ... x = 1 ... def inner(): ... print(x) # 1 ... inner() # 2 ... >>> outer() 1 >>>
A closure that takes a function as a parameter and returns a function is called a decorator. Consider an example of useful decorators.
>>> >>> def outer(some_func): ... def inner(): ... print("before some_func") ... ret = some_func() # 1 ... return ret + 1 ... return inner ... >>> def foo(): ... return 1 ... >>> decorated = outer(foo) # 2 >>> >>> decorated() before some_func 2 >>>
A decorated variable is a decorated version of foo. In fact, we could replace foo with its decorated version and not learn the new syntax by simply reassigning the variable containing our function:
>>> >>> foo = outer(foo) >>> >>> foo # doctest: +ELLIPSIS <function outer.<locals>.inner at 0x...> >>>
Now we have a beautiful decorator for tracking function calls. Decorators can be used to work with any programming language using Python. This is an extremely useful tool, the mechanism of work of which must be understood in order to correctly apply them.
Source: https://habr.com/ru/post/460931/
All Articles