Home  >  Article  >  Backend Development  >  Python decorator execution order myth

Python decorator execution order myth

高洛峰
高洛峰Original
2017-02-17 11:02:441000browse

Exploring the execution order of multiple decorators

Decorators are tools used by Python to encapsulate functions or codes. You can find many articles on the Internet to learn from. What I want to discuss here is the execution of multiple decorators. A myth about sequence.

Question

Most of the function calling sequences involving multiple decorators will indicate that they are top-down, such as the following example:

def decorator_a(func):
    print 'Get in decorator_a'
    def inner_a(*args, **kwargs):
        print 'Get in inner_a'
        return func(*args, **kwargs)
    return inner_a

def decorator_b(func):
    print 'Get in decorator_b'
    def inner_b(*args, **kwargs):
        print 'Get in inner_b'
        return func(*args, **kwargs)
    return inner_b

@decorator_b
@decorator_a
def f(x):
    print 'Get in f'
    return x * 2

f(1)

The above code First define the two functions: decotator_a, decotator_b. The function of these two functions is to receive a function as a parameter and then return another created function. The received function is called in this created function (text is more confusing than code) . The last defined function f uses decotator_a, decotator_b defined above as decoration functions. After we call the decorated function f with 1 as the parameter, what is the order of decotator_a, decotator_b (here, in order to indicate the order of function execution, print output is used to view the order of function execution)?

If you judge based on the bottom-up principle without thinking, execute decorator_a first and then decorator_b, then Get in decotator_a, Get in inner_a will be output first, and then Get in decotator_b, Get in inner_b. However, it is not.

The actual running results are as follows:

Get in decorator_a
Get in decorator_b
Get in inner_b
Get in inner_a
Get in f

The difference between functions and function calls

Why is inner_b executed first and then inner_a? In order to completely understand the above problem, we must first distinguish between two concepts: functions and function calls. In the above example, f is called a function, and f(1) is called a function call. The latter is the result of evaluating the parameters passed in by the former. In Python, a function is also an object, so f refers to a function object, and its value is the function itself. f(1) is a call to the function, and its value is the result of the call. According to the definition here, f(1) The value is 2. Similarly, taking the decorator_a function above as an example, it returns a function object inner_a, which is defined internally. The function func is called in inner_a, and the result of the call to func is returned as a value.

The decorator function is executed immediately after the decorated function is defined

The second question that needs to be clarified is what exactly happens when the decorator decorates a function. Now simplify our example, assuming it is as follows:

def decorator_a(func):
    print 'Get in decorator_a'
    def inner_a(*args, **kwargs):
        print 'Get in inner_a'
        return func(*args, **kwargs)
    return inner_a

@decorator_a
def f(x):
    print 'Get in f'
    return x * 2

As said in many articles introducing decorators:

@decorator_a
def f(x):
    print 'Get in f'
    return x * 2

# 相当于
def f(x):
    print 'Get in f'
    return x * 2

f = decorator_a(f)

So, when the interpreter executes this code, decorator_a has Called, it takes function f as a parameter and returns a function generated internally, so f refers to inner_a returned in decorator_a. So when f is called in the future, it is actually equivalent to calling inner_a. The parameters passed to f will be passed to inner_a. When calling inner_a, the received parameters will be passed to the func in inner_a, that is, f. The final return is the one called by f. value, so on the outside it looks like calling f directly again.

Explanation of questions

When you clarify the above two concepts, you can clearly see what happened in the most original example.
When the interpreter executes the following code, it actually calls decorator_a and decorator_b in order from bottom to top, which will output the corresponding Get in decorator_a and Get in decorator_b. At this time f is equivalent to inner_b in decorator_b. But because f has not been called, inner_b has not been called, and by analogy, inner_a inside inner_b has not been called, so Get in inner_a and Get in inner_b will not be output.

@decorator_b
@decorator_a
def f(x):
    print 'Get in f'
    return x * 2

Then in the last line, when we call f with parameter 1, inner_b is called. It will first print Get in inner_b, and then call inner_a inside inner_b, so it will print Get in inner_a, Then inner_a calls the original f internally, and returns the result as the final result. At this point you should know why the output is like that, and have a certain understanding of what actually happens in the decorator execution sequence.

When we remove the call of f in the last line of the above example and put it in the repl for demonstration, we can also naturally see the order problem:

➜  test git:(master) ✗ python
Python 2.7.11 (default, Jan 22 2016, 08:29:18)
[GCC 4.2.1 Compatible Apple LLVM 7.0.2 (clang-700.1.81)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import test13
Get in decorator_a
Get in decorator_b
>>> test13.f(1)
Get in inner_b
Get in inner_a
Get in f
2
>>> test13.f(2)
Get in inner_b
Get in inner_a
Get in f
4
>>>

In actual application scenarios, when We wrote two decoration methods in the above way. For example, first verify whether you are logged in @login_required, and then verify whether the permissions are sufficient @permision_allowed. We use the following order to decorate the function:

@login_required
@permision_allowed
def f()
  # Do something
  return

More Python decorations For articles related to the myth of the execution order of the device, please pay attention to the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn