Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding Closures For Decorators

Tags:

python

I'm trying to write a decorator that takes a few arguments, and can decorate arbitrary functions. After reading a few code examples, and stepping through the debugger I've figured out how to write it. But I don't fully understand why it works.

def bar(arg1):

    def inner_bar(f):

        def inner_inner_bar(*args, **kwargs):
            new_args = (x + arg1 for x in args)

            return f(*new_args, **kwargs)

        return inner_inner_bar

    return inner_bar


@bar(4)
def foo(x, y):
    print("Sum is {0}".format(x+y))

if __name__ == "__main__":
    foo(1, 2)


Sum is 11

What I don't fully grasp is how/why the function f exists in the scope of inner_bar but not bar. And similarly that args and kwargs exist in the scope of inner_inner_bar but not inner_bar.

What is Python doing when I use @bar that makes the different variables available in the different methods of my decorator?

like image 417
Batman Avatar asked May 26 '16 19:05

Batman


1 Answers

What is Python doing when I use @bar that makes the different variables available in the different methods of my decorator?

Note that you're not just using @bar, you're using @bar(4). It works like this:

  • bar(4) returns a function (inner_bar)
  • Using @bar(4) to decorate foo calls inner_bar(foo).
  • inner_bar(foo) returns a function (inner_inner_bar)
  • This function (inner_inner_bar) is assigned back to the name foo
  • When you call foo, you are calling inner_inner_bar, so whatever arguments you pass are passed as the *args and **kwargs

What Python is "doing" is calling the functions involved. All of the variables you're asking about (f, args and kwargs) are just function arguments, which, as usual, become available when their function is called. f becomes available when inner_bar is called, namely when you apply the decorator. *args and **kwargs become available when inner_inner_bar is called, namely when you call the decorated function. The only thing that is available when you write bar(4) is arg1, because the other functions haven't been called yet.

like image 83
BrenBarn Avatar answered Sep 23 '22 01:09

BrenBarn