Docorator Disected

El Pitonero pitonero at gmail.com
Sat Apr 2 10:22:39 EST 2005


Ron_Adam wrote:
>
> # (0) Read defined functions into memory
>
> def decorator(d_arg):     # (7) Get 'Goodbye' off stack
>
>     def get_function(function): # (8) Get func object off stack
>
>         def wrapper(f_arg):        # (9) Get 'Hello' off stack
>
>             new_arg = f_arg+'-'+d_arg
>             result = function(new_arg)  # (10) Put new_arg on stack
>                                         # (11) Call func object
>
>             return result          # (14) Return result to wrapper
>
>         return wrapper        # (15) Return result to get_function
>
>     return get_function    # (16) Return result to caller of func
>
>
>
> @decorator('Goodbye')   # (5) Put 'Goodbye' on stack
>                         # (6) Do decorator
>
> def func(s):            # (12) Get new_arg off stack
>
>     return s            # (13) Return s to result
>
> # (1) Done Reading definitions
>
>
> print func('Hello')     # (2) Put 'Hello' on stack
>                         # (3) Put func object on stack
>                         # (4) Do @decorator
>                         # (17) print 'Hello-Goodbye'
>
> # Hello-Goodbye

Is it possible that you mistakenly believe your @decorator() is being
executed at the line "func('Hello')"?

Please add a print statement to your code:

def decorator(d_arg):
     def get_function(function):
         print 'decorator invoked'
         def wrapper(f_arg):
             new_arg = f_arg+'-'+d_arg
             result = function(new_arg)
             return result
         return wrapper
     return get_function

When you run the program, you will see that the comment "decorator
invoked" is printed out at the moment when you finish defining:

@decorator('Goodbye')
def func(s):
    return s

That is, decorator is invoked before you run the line "func('Hello')".

Decorator feature is a metaprogramming feature. Not a programming
feature. By metaprogramming I mean you are taking a function/code
object, and try to do something with it (e.g., wrap it around.) By the
time you finish defining the function "func(s)", the decorator
"get_function()" was already invoked and will never be invoked again.

It's better to view functions as individual objects. And try to think
who holds reference to these objects. If no one holds reference to an
object, it will be garbage collected and will be gone. After you define
the function "func()" and before you execute "func('Hello')", this is
the situation:

decorator() <--- held by the module
get_function() <--- temporary object, garbage collected
wrapper() <--- held by the module, under the name "func"
func() <--- held by wrapper(), under the name "function"

'Goodbye' <--- string object, held by the wrapper function object,
under the name d_arg

Objects can be rebound to different names. In your code you have
rebound the original wrapper() and func() function objects to different
names.

I think the confusing part is that, for function name binding, Python
does not use the = operator, but instead relies on the "def" keyword.
Maybe this is something to be considered for Python 3K. Anonymous
function or codeblock objects are good to have, when you are doing
metaprogramming.




More information about the Python-list mailing list