Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the logical execution order of this Python decorated function?

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.

like image 686
Geo Avatar asked Feb 22 '23 15:02

Geo


1 Answers

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().

like image 86
NPE Avatar answered Mar 29 '23 22:03

NPE