简体   繁体   中英

Control Flow in decorator

I am having trouble in understanding the flow of control in a Python program having a decorator.

def executor(func):
    def innerExecutor():
        #print("inside executor")
        print("------------------------")
        func()
        print("------------------------")
    return innerExecutor

@executor
def multiply():
    n1 = 10
    n2 = 20
    print("multiplication = ", (n1 * n2))

multiply()

What trouble I am having with is: When the executor() function is called, and it returns the reference of innerExecutor() , where is this reference stored? When is the control actually passed to the inner function? I am very new in Python.

The syntax

@some_decorator
def somefunc():
   ...

is equivalent to:

somefunc = some_decorator(somefunc)

This is possible in Python because functions are first-class objects . They can be passed as arguments to, and returned from, other functions - so-called higher-order functions .

In your example, the final call to multiply() actually runs innerExector() , a function created by the decorated definition of multiply() . To understand what's going on here, take a closer look at the definition of executor :

def executor(func):
    def innerExecutor():
        #print("inside executor")
        print("------------------------")
        func()
        print("------------------------")
    return innerExecutor

When the code:

@executor
def multiply():
    ...

is run, the function executor is called with the function multiply as its argument. executor defines the function innerExecutor() , and returns it, but does not execute it . This instance of innerExecutor() is called in place of all future references to multiply() .

But wait - usually, we don't want to completely replace the function being decorated. innerExecutor() needs to call the decorated function at some point. Recall that a decorated function definition:

@executor
def multiply():
    ...

is equivalent to:

def multiply():
    ...
multiply = executor(multiply)

Passing the function to be decorated as an argument to the decorator, allows the definition of innerExecutor() to call it. But wait - innerExecutor() isn't called until much later, after executor() has returned and it's argument func has gone out of scope. So why does it work?

Thanks to another feature of Python called a closure . What this means is the inner function can refer to local variables defined in the enclosing scope - including arguments passed into the outer function executor - even after exiting the enclosing scope . The magical thing about closures is that the interpreter detects the dependency of the inner function definition on a local variable of the outer function, and keeps it available even after executor() returns. This link goes into more detail about closures.

Finally, the call to multiply() in the last line actually calls the instance of innerExecutor() that was created when the decorated definition was run, which in turn calls the original undecorated version of multiply() .

Every time a decorator is used to decorate a function definition, it creates a new instance of the inner function, which replaces the undecorated function. So if you decorate 5 different functions, 5 different instances of innerExecutor() are created. Later, when any of those decorated functions are called, it will instead call instead the appropriate instance of innerExecutor() , which in turn calls the corresponding undecorated function.

it returns the reference of innerExecutor(), where is this reference stored?

When is the control actually passed to the inner function?

It is not stored in anywhere, it is called as below:

innerExecutor(multiply)

That's why your decorator method needs to return its own reference, otherwise, it would be equivalent to this:

None(multiply)  # TypeError: 'NoneType' object is not callable

Here's the documentation of aboves behavior

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM