How pass a function with parameters as a parameter to another function in Python?

Viewed 100

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

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).

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")
Related