1

(This is a newbie Python question)

As I tried the Python decorator with code below, I am a bit puzzled how Python map the Test method name to the parameters at different levels. How does Python know the Test method should be map to name2? Is it because name1 has already been provided with "tom"? But it seems to be a naive reason.

I know the @decorator is just a syntax sugar. What's going on behined the scene?

def level1(name1):
    print("in level1 with %s" % name1)
    def level2(name2):
        print("in level2 with %s..." % name2)
        def level3():
            return "nonthing is done..."
        return level3        
    return level2



@level1("tom")
def Test():
    print("MyClass.Test is callled")
    return "I am Result"

if(__name__ =="__main__"):
    print("Result is: %s" % Test())

The output is:

in level1 with tom
in level2 with <function Test at 0x0000000001DD33C8>...
Result is: nonthing is done...
smwikipedia
  • 61,609
  • 92
  • 309
  • 482

1 Answers1

2

By writing @level1("tom"), you call the function @level1 with argument "tom". This assigns "tom" to the argument of level1, namely name1. This is just the same as what would happen if you didn't use it as a decorator and just called level1("tom").

Since you used the expression level1("tom") as a decorator, the result of this expression --- that is, the return value of the call to level1 --- is itself called with the decorated function (Test) as its argument. In this type of construct, level1 is not really a decorator. Rather, it is a decorator-maker, or decorator factory. The return value of level1 is what actually decorates Test. In this case the return value of level1 is the function level2. Remember that using decorator like this:

@deco
def fun():
    # blah

is the same as this:

def fun():
    # blah
fun = deco(fun)

In your case, the decorator is the return value of level1, namely level2. That means that your code is equivalent to:

deco = level1("tom")
def Test():
    print("MyClass.Test is callled")
    return "I am Result"
Test = deco(Test)

deco is thus level2, since that is what level1 returns. Since Test is what is passed to level2, it is assigned to the argument of level2, which is name2.

You may find this question on the same topic to be helpful as well.

Community
  • 1
  • 1
BrenBarn
  • 242,874
  • 37
  • 412
  • 384
  • Technically, I guess the `decorator-maker chain` could be of any depth. And Python just keeps calling the method until it finds a method without an argument passed in by programmer. Then Python will pass in the decorated method as the argument. – smwikipedia Oct 14 '14 at 09:34
  • @smwikipedia: Not sure what you mean. Do you mean that you cannot do `@nested_maker()()()`? The decorator expression cannot be a full expression but is limited to a series of attribute references (i.e., a dotted name) optionally followed by a single argument list. So you can have `one.two.three('blah')` but not `one['two']('blah')` or `one('two')('three')`. – BrenBarn Oct 14 '14 at 18:11