2

I'm studying decorators and have came through this example that i can't figure it out how it's possible to access the function's parameters sent as parameter (function display_info) within the wrapper_function without receveing then as argument on first place on decorator_function.

(I think I understand the concept of *args and **kwargs, but in the example below it the decorator function only gets one argument to work with, but wrapper that's within access *args that represent the parameters sent along side with display_info).

def decorator_function(originla_function):
    def wrapper_function(*args, **kwargs):
        #how wrapper accessed the arguments that weren't received on decorator_function
        print('wrapper executed before {}'.format(originla_function.__name__))
        return originla_function(*args, **kwargs)
    return wrapper_function

@decorator_function
def display_info(name, age):
    print('display_info has the following arguments ({}, {})'.format(name, age))

display_info('Bob', 29)

2 Answers 2

2

You have to understand that:

@decorator_function
def display_info(name, age):
    # ...

is basically just:

def display_info(name, age):
    # ...

display_info = decorator_function(display_function)

decorator_function transforms the display_function function into another function. This is done only once. That's it, nothing more. From now on, what matters is what decorator_function returned, not how decorator_function was called in order to create this new function.

Now, what happens behind the scenes in your example?

@decorator_function
def display_info(name, age):

replaces display_info with the result of decorator_function(display_info). What does decorator_function(display_info) return? This function:

def wrapper_function(*args, **kwargs):
    #how wrapper accessed the arguments that weren't received on decorator_function
    print('wrapper executed before {}'.format(display_info.__name__))
    return display_info(*args, **kwargs)

Notice that I replaced originla_function with display_info because the originla_function parameter of decorator_function is the original display_info.

So running display_info('Bob', 29) calls the new display_info, i.e. wrapper_function('Bob', 29). When wrapper_function is called like this, *args will be ('Bob', 29) and **kwargs** will be {}.

 return display_info(*args, **kwargs)

means it returns the result of calling the original display_info. More precisely display_info(*('Bob', 29), **{}) which is the same as display_info('Bob', 29).

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks Cristian, i was missing this step display_info = decorator_function(display_function), now it's clear that when i run display_info decorated i'm actually calling wrapper with its parameters. Thanks again, I was missing a really basic concept !
The glossary and PEP 318 are the reference documentation for this.
0

In your decorator just access args[0] and args[1] - those are positional arguments you pass as to any other method or function:

print(args[0], args[1])

in the decorator outputs

Bob 29

so in your decorator:

def decorator_function(originla_function):
    def wrapper_function(*args, **kwargs):
        # Output positional arguments args[0] and args[1] or implement logic processing them
        print(args[0], args[1])
        print('wrapper executed before {}'.format(originla_function.__name__))
        return originla_function(*args, **kwargs)
    return wrapper_function

*args are your positional (only them you're passing in your case), and **kwargs are the keyword arguments, which you're not using in your example.

If you want to use a parametrized decorator that uses parameters passed to it, here's an example:

def tags(tag_name):
    def tags_decorator(func):
        def func_wrapper(name):
            return "<{0}>{1}</{0}>".format(tag_name, func(name))
        return func_wrapper
    return tags_decorator

@tags("p")
def get_text(name):
    return "Hello "+name


if __name__=='__main__':
   print get_text("John")

5 Comments

But on decorator_function's definition, i just received the original_function as argument, how on wrapper definitions that it's a function within it is able to access the positional arguments *args.
It's the wrapper_function, not decorator_function, that receives your actual arguments - the decorator_function receives the function itself. Decorating is attaching additional behavior to the original call.
Maybe i'm not been able to make myself clear, but what i don't get it's that the decorator_function receives the original function, and wrapper that's within decorator_function access *args, as i understand the decorator function should have explicit received the arguments that any function within would be able to access.
Thank you for your reply, but based on what you illustrated, on tags definition it receives (tag_name) and on tag_decorator utilizes (func), how on earth that definition can have that parameter that's related to the parameter passed trough calling get_text when tags didn't receive it on first place but the inner function proceed to be able to utilize it.
The way decoration in Python works is the outer scope receives the actual function, and the inner scope definition receives attaches the behavior to that function, receiving the positional and keyword arguments. In the inner scope you return updated function call, and in the outer you return the actual function definition. That reduces to syntactic sugar used as decorator.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.