4

Imagine this:

def b1(fnc):
  print "b1"
  return fnc

@b1
def a1():
  print "a1"

if __name__ == "__main__":
  a1() # will print b1 a1

So, when I'm using @b1, a1 gets turned to a1 = b1(a1), right? Then, when I say:

a1()

This turns to:

b1(a1)

And then goes into:

print "b1"
return fnc

Where/Who exactly is calling fnc? I have a feeling I'm asking a very stupid question, but I want to understand.

Geo
  • 93,257
  • 117
  • 344
  • 520

4 Answers4

7

The decorator is only executed once. It takes a callable object and returns a callable object. The object that it returns is used in place of a1.

In other words, b1 is called at the point where a1 is defined. It prints out "b1" and returns a1 unchanged. Since it returns a1 unchanged, b1 plays no role whatsoever in any subsequent calls to a1.

Therefore, the following comment isn't quite correct:

  a1() # will print b1 a1

In fact,

  • a1() only prints "a1".
  • The "b1" that you see is printed by @b1 / def a1():.

If you change the code like so and re-run, the sequence of events should become clearer:

def b1(fnc):
  print "b1"
  return fnc

@b1
def a1():
  print "a1"

if __name__ == "__main__":
  print 'in __main__'
  a1()
  a1()

Finally, to achieve the effect you were looking for, the decorator would need to return a different callable object:

def b1(fnc):
  def decorated():
    print "b1"
    return fnc()
  return decorated

@b1
def a1():
  print "a1"

if __name__ == "__main__":
  print 'in __main__'
  a1()
  a1()

Here, it is pretty clear who is calling fnc().

NPE
  • 486,780
  • 108
  • 951
  • 1,012
  • The key idea is that a function definition is just another peace of code that gets executed (usually only once). The decorator is only execute when the function definition is executed. – Björn Pollex Oct 24 '11 at 13:05
1

Who is calling fnc?

Basically, the () behind a1 in the last line of the code.

So what is going on? Decorators are meant to change the behavior of functions.

So when you decorate something, a new function is created based on what the decorator returns. This means the decorator runs once when a1 is defined. Here is an example that demonstrates this:

print 'def b1'
def b1(fnc):
  print "b1"
  return fnc

print 'def a1'
@b1
def a1():
  print "a1"

if __name__ == "__main__":
  print 'main'
  a1() # will print b1 a1

This will print:

def b1
def a1
b1
main
a1

As you can see, the decorator b1 is called before main is executed.

It returns a function instance which is assigned to the alias a1 which can be used like any other function instance.

This what the "call operator" () after the a1 in the last line of the code does.

Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
0

Check out the top answer of How to make a chain of function decorators?

In that you'll notice that the decorator definition is actually transforming the function, and returning the transformed function. That is executed once upon decoration (when the @... is applied). In the example you give, b1 is not defining a transformation but the print statement still gets executed. The original function is then returned, therefore a1 does not get "redefined". the action of the b1 decorator then is to do nothing to a1.

Community
  • 1
  • 1
Benedict
  • 2,771
  • 20
  • 21
0

Let's call a1_prev, the a1 function before being decorated, a1() turns into

b1(a1_prev)()

and not only b1(a1_prev), the last () are those who call the func returned by b1.

Patrick Browne
  • 5,290
  • 4
  • 16
  • 14