2

I have a question about decorators. I understand what are decorators and I know how to use it, I have read all this tutorial How to make a chain of function decorators?

I understand that :

>>> def my_decorator(fn):
>>>     print 'Do something before'
>>>     print fn()


>>> def foo():
>>>     return 'Hello World!'

>>> foo = my_decorator(foo)

Is the same at that :

>>> def my_decorator(fn):
>>>     print 'Do something before'
>>>     print fn()

>>> @my_decorator
>>> def foo():
>>>     return 'Hello World!'

I know what are closures and why we use closure in a decorator with parameters (to get the decorator parameters in nested function) but that I don't understand is why we use closure and nested functions to get arguments and the function.

How the closure (or something else) can access parameters and the function outside. I am unable to do the same without the @decorator.

Here for example I can access my foo() and the parameters of the function without passing this function in parameter :

def my_decorator(str):
    def wrapper(fn):
        def inner_function(*args):
            print 'Do something before'
            return fn(*args)
        return inner_function
    return wrapper

@my_decorator('test')
def foo(a, b):
    return a + b


print foo(1, 1)

How this is possible ?

Community
  • 1
  • 1
Freelancer
  • 4,459
  • 2
  • 22
  • 28
  • Because that's what a decorator does. It's not a simple function call, it's a decorator, and it gets passed the function below it as the first argument (and the returned function replaces it). – Gareth Latty Aug 06 '13 at 13:22
  • It's not magic exactly - it's like asking why the brackets after a function call it - it's a documented part of the language. – Gareth Latty Aug 06 '13 at 13:23
  • What do you mean by "I am unable to do the same without the @decorator"? Please give an example. – Janne Karila Aug 06 '13 at 13:25
  • As in the first example I can see what is a decorator – Freelancer Aug 06 '13 at 13:26
  • 6
    You can do `foo = my_decorator('test')(foo)`, as an alternative to the `@` syntax. – Janne Karila Aug 06 '13 at 13:27
  • Thanks this helped I posted the solution – Freelancer Aug 06 '13 at 13:49
  • You are slightly wrong about how decorators work; `@decorator def foo()...` is equivalent to `foo = decorator(foo)`, not just `decorator(foo)`. (Which is pretty much what Janne says). – chepner Aug 06 '13 at 13:50
  • Thanks it's was just for me to see the result. I know that it used to modify the function at the definition. So it should replace my function definition. I edited my question. Thanks for pointed that – Freelancer Aug 06 '13 at 13:59

2 Answers2

-1

Decorator is a function that takes one argument (this argument is function) - this is my definition of decorator. In your case wrapper is decorator. While my_decorator is used to collect arguments for inner_function. inner_function is used to replace original [not decorated] function. Step by step explanation would be:

  1. my_decorator is called to collect options for inner_function
  2. my_decorator returns wrapper
  3. wrapper is responsible for capturing original function or in other words decorate it. In your case original function is foo.
  4. wrapper returns replacement function for original (which is inner_function)
  5. from now foo points to inner_function, therefore inner_function is executed then foo is called

Hope it makes things a bit clearer.

aisbaa
  • 9,867
  • 6
  • 33
  • 48
-1

I found the solution :

In fact the decorator use the closure functionality : So here is the solution to do the same thing without the decorator and with parameters (it's just to understand the operation, and to learn)

def wrapper(str):
    def decorator_factory(fn):
        def inner_function(*args):
            print 'Do something before'
            return fn(*args)
        return inner_function
    return decorator_factory

@my_decorator('test')
def foo(a, b):
    return a + b

# with decorator
print foo(1, 1)

# without decorator
print wrapper('str')(foo)(1, 1)
Freelancer
  • 4,459
  • 2
  • 22
  • 28
  • 2
    Another way to look at it is: `the_actual_decorator = my_decorator('some_string')`. You see, the function `my_decorator()` is somewhat poorly named (for this example) because it is not really a decorator per se, but a wrapper that returns a decorator with a closure. This *returned* decorator is the one that is applied to `foo()`. – Joel Cornett Aug 06 '13 at 14:15
  • So I have to name my_decorator() in wrapper() ? – Freelancer Aug 06 '13 at 14:22