1

I am trying to understand if decorators are always invoked in the order they are encountered while reading the source code from top to bottom? Here is some sample code I wrote:

def log(func):
    print(func)
    return func

class A:
    @log
    def __init__(self):
        print('__init__')

    @log
    def foo(self):
        print('foo')

    @log
    def bar(self):
        print('bar')

    @log
    def baz(self):
        print('baz')

    @log
    def qux(self):
        print('qux')

Here is the output:

<function A.__init__ at 0x0000000002342840>
<function A.foo at 0x00000000023428C8>
<function A.bar at 0x0000000002342950>
<function A.baz at 0x00000000023429D8>
<function A.qux at 0x0000000002342A60>

The output above seems to indicate that that decorators are invoked in the order they are encountered while reading the source code from top to bottom?

Lone Learner
  • 18,088
  • 20
  • 102
  • 200
  • possible duplicate of [How can I make a chain of function decorators in Python?](http://stackoverflow.com/questions/739654/how-can-i-make-a-chain-of-function-decorators-in-python) – Paulo Bu Mar 15 '14 at 16:21
  • 1
    Decorators are a fancy way of writing `baz = log(undecorated_baz)`, so yes, the decorator functions are called in the right order. – Blender Mar 15 '14 at 16:26
  • It's important to understand that `def`, `class`, and decorators are *executable statements*. There is no such thing as a declaration. When a module is imported, it is parsed and the top-level statements are executed in "from top to bottom", but each of those statements has the full power of Python and can jump around with any control flow permitted by the language. –  Mar 15 '14 at 16:33
  • It's easy to read the title, and think it's about chaining decorators. But the example is just about executing a class definition. – hpaulj Mar 15 '14 at 16:43
  • @PauloBu What? Where do you see decorator chaining in this question? – Lone Learner Mar 16 '14 at 04:24
  • 1
    @LoneLearner The title mislead me. Anyways you should read that answer. Is a very good source of information about decorators. – Paulo Bu Mar 16 '14 at 07:51

3 Answers3

2

In your case they are being read "in order" but you only have one decorator per method. A good way to illustrate how they are read is when they are chained, or there is more than one. For example:

@i_get_called_last
@i_get_called_second
@i_get_called_first
def my_decorated_function(a):
    print a
    return a

In this example you can read they actually get read from "bottom to top", meaning from inner-most to outer-most in relation to the method.

Drewness
  • 5,004
  • 4
  • 32
  • 50
  • 2
    And that's just function composition: `func = last(second(first(func)))`. – Blender Mar 15 '14 at 16:35
  • I don't agree this is bottom to top. This is still top to bottom when you look at it this way: `my_decorated_function = i_get_called_last(i_get_called_second(i_get_called_first(my_decorated_function)))`. – Lone Learner Mar 16 '14 at 04:26
  • @LoneLearner - Probably the _best_ way to think of it is "inner to outer". We're getting down to semantics here but inner to outer best describes it when it's written `i_get_called_last(i_get_called_second(i_get_called_first(my_decorated_function))‌​)` IMHO. – Drewness Mar 16 '14 at 14:48
0

Yes, but there are nontrivia situation:

@log
def a():
  @log
  def b():
    pass
  pass

output: only

<function a at 0x7fe2dfe24710>

And another:

def log1(func):
  print('1', func)
  return func

def log2(func):
  print('2', func)
  return func

@log1
@log2
def a():
  pass

output:

2 <function a at 0x7fe2dfe24b90>
1 <function a at 0x7fe2dfe24b90>
mingaleg
  • 2,017
  • 16
  • 28
-1

It seems like you have a more fundamental thing to understand in Python:

  1. All code is execute top to bottom
  2. Even on the compiled byte code file, there are no Classes, or functions present
  3. Classes and functions are created in Python in the order they are found in the code, as each file is imported.

Once you understand that, and that decorators are just the equivalent of replacing a declared function with the return value of the decorator, i.e.:

@deco
def myfunc():
   pass

# is equivalent to:

def myfunc():
   pass
myfunc = deco(myfunc)

You start to understand what is going on. SO, when Python compiler finds a block in source code like:

def a(): pass

It records in bytecode the code object for the function body (the pass statement), and the bytecode to create a function object from this code, and the elements contained in the def statement (essentially a call to types.FunctionType). On running the code (after the compile pass), the returned object of this call is then bound to the name a in the namespace where the function was found. Only at this point the "function object" is existing, and can be called.

If there are decorators preceding the def statement, they are called in bottom-to-top order, before the name is actually bound - the function name is just bound to the returned object of the last decorator.

A different thing goes for class bodies:

  1. When finding a class block, the interpreter creates a new namespace
  2. it enters the class body and starts executing the lines there, in order (it does not execute the body of functions, just compile then to bytecode)
  3. So one could even put for or while loops directly inside class bodies - those loops would run at class creation time (usually when the module is imported).
  4. And - methods inside the class body are created as functions as the class is executed, as described above
  5. when the class body is over, Python calls the built-in type factory to actually create the class: the parameters are the class name, class bases, and the names of objects (functions and attributes alike) found inside the class body as a dictionary.
  6. The returned object of this call to type (or rather, to the class' metaclass) is bound to the class name in the namespace where the "class" statement is.
  7. If there are class decorators, they are called with the class object returned, before the name binding step,as it happens with functions.

(this is actually simpler than creating functions, and you can actually find a lot more code in the wild which does create a new class without a class body, calling type instead, than code that creates new functions calling FunctionType)

So - answering your question: yes - "decorators are called in the order they are encountered" - but that is natural once you find out how Python works.

So, if the above explanation is a bit vague, check what this does:

def log(func):
    print(func.__name__)
    return func

class A(object):
   for i in range(5):
       @log
       def func(self): 
          pass
       locals()["func_%d" % i] = func

And compare it with:

class B(object):
   for i in range(5):
       def func(self): 
          pass
       func.__name__ = "func_%d" % i
       locals()["func_%d" % i] = log(func)
jsbueno
  • 99,910
  • 10
  • 151
  • 209
  • "So, if the above explanation is a bit vague", I don't think so. Not sure why the down votes. – Drewness Mar 16 '14 at 14:49
  • I am trying to understand the downvotes. Given the question typed, the O.P. needs to understand these concepts to program properly in the language. Otherwise, the "correct answer" to "Are decorators always invoked in the order they are encountered while reading the source code from top to bottom?" is a simple, plain "yes" - but that would not help much. – jsbueno Mar 17 '14 at 04:02