Python | functools.wraps() function

23 Sep, 2021
functools is a standard Python module for higher-order functions (functions that act on or return other functions). wraps() is a decorator that is applied to the wrapper function of a decorator. It updates the wrapper function to look like wrapped function by copying attributes such as __name__, __doc__ (the docstring), etc. 
Syntax: @functools.wraps(wrapped, assigned = WRAPPER_ASSIGNMENTS, updated = WRAPPER_UPDATES)Parameters: wrapped: The function name that is to be decorated by wrapper function. assigned : Tuple to specify which attributes of the original function are assigned directly to the matching attributes on the wrapper function. By default set to WRAPPER_ASSIGNMENTS (which assigns to the wrapper function’s __module__, __name__, __qualname__, __annotations__ and __doc__, the documentation string) updated : Tuple to specify which attributes of the wrapper function are updated with the corresponding attributes from the original function. By default set to WRAPPER_UPDATES (which updates the wrapper function’s __dict__, i.e. the instance dictionary).  
Example 1: Without functools.wraps() 
Python3
def a_decorator(func):    def wrapper(*args, **kwargs):        """A wrapper function"""        # Extend some capabilities of func        func()    return wrapper @a_decoratordef first_function():    """This is docstring for first function"""    print("first function") @a_decoratordef second_function(a):    """This is docstring for second function"""    print("second function") print(first_function.__name__)print(first_function.__doc__)print(second_function.__name__)print(second_function.__doc__)
wrapper
A wrapper function
wrapper
A wrapper function
 
Now what will happen if we write help(first_function) and help(second_function) 
Python3
print("First Function")help(first_function) print("\nSecond Function")help(second_function)
First Function
Help on function wrapper in module __main__:

wrapper(*args, **kwargs)
    A wrapper function


Second Function
Help on function wrapper in module __main__:

wrapper(*args, **kwargs)
    A wrapper function
 
While the above code will work logically fine, but consider this if you are writing an API or a library and someone want to know what your function does and it’s name or simply type help(yourFunction), it will always show wrapper function’s name and docstring. This gets more confusing if you have used the same wrapper function for different functions, as it will show the same details for each one of them. Ideally it should show the name and docstring of wrapped function instead of wrapping function. Manual solution would be to assign __name__, __doc__ attributes in the wrapping function before returning it. 
Python3
def a_decorator(func):    def wrapper(*args, **kwargs):        """A wrapper function"""        # Extend some capabilities of func        func()    wrapper.__name__ = func.__name__    wrapper.__doc__ = func.__doc__    return wrapper @a_decoratordef first_function():    """This is docstring for first function"""    print("first function") @a_decoratordef second_function(a):    """This is docstring for second function"""    print("second function") print(first_function.__name__)print(first_function.__doc__)print(second_function.__name__)print(second_function.__doc__)
first_function
This is docstring for first function
second_function
This is docstring for second function
 
This solves the problem, but what if we again type help(yourFunction), 
Python3
print("First Function")help(first_function) print("\nSecond Function")help(second_function)
For first_function: help(first_function)  
First Function
Help on function first_function in module __main__:

first_function(*args, **kwargs)
    This is docstring for first function


Second Function
Help on function second_function in module __main__:

second_function(*args, **kwargs)
    This is docstring for second function
 
As you can see it still has an issue, i.e. the signature of the function, it is showing signature used by wrapper function (here, generic signature) for each of them. Also if you are implementing many decorators, then you have to write these lines for each one of them. So to save time and increase readability, we could use functools.wraps() as decorator to wrapper function.Example (with functools.wraps())  
Python3
from functools import wraps def a_decorator(func):    @wraps(func)    def wrapper(*args, **kwargs):        """A wrapper function"""         # Extend some capabilities of func        func()    return wrapper @a_decoratordef first_function():    """This is docstring for first function"""    print("first function") @a_decoratordef second_function(a):    """This is docstring for second function"""    print("second function") print(first_function.__name__)print(first_function.__doc__)print(second_function.__name__)print(second_function.__doc__)
first_function
This is docstring for first function
second_function
This is docstring for second function
 
Now, if we type help(first_function) – 
Python3
print("First Function")help(first_function) print("\nSecond Function")help(second_function)
First Function
Help on function first_function in module __main__:

first_function()
    This is docstring for first function


Second Function
Help on function second_function in module __main__:

second_function(a)
    This is docstring for second function
 
gabaa406
Python Decorators
Python
Writing code in comment?
Please use ide.geeksforgeeks.org,
generate link and share the link here.