Python | functools.wraps() function

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 coping 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()

filter_none

edit
close

play_arrow

link
brightness_4
code

def a_decorator(func):
    def wrapper(*args, **kwargs):
        """A wrapper function"""
        # Extend some capabilities of func
        func()
    return wrapper
  
@a_decorator
def first_function():
    """This is docstring for first function"""
    print("first function")
  
@a_decorator
def 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__)

chevron_right


Output:



wrapper
A wrapper function
wrapper
A wrapper function

Now what will happen if we write help(first_function) and help(second_function)

filter_none

edit
close

play_arrow

link
brightness_4
code

print("First Function")
help(first_function)
  
print("\nSecond Function")
help(second_function)

chevron_right


Output:

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.

filter_none

edit
close

play_arrow

link
brightness_4
code

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_decorator
def first_function():
    """This is docstring for first function"""
    print("first function")
  
@a_decorator
def 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__)

chevron_right


Output:

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

filter_none

edit
close

play_arrow

link
brightness_4
code

print("First Function")
help(first_function)
  
print("\nSecond Function")
help(second_function)

chevron_right


For first_function: help(first_function)

Output:

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

filter_none

edit
close

play_arrow

link
brightness_4
code

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_decorator
def first_function():
    """This is docstring for first function"""
    print("first function")
  
@a_decorator
def 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__)

chevron_right


Output:

first_function
This is docstring for first function
second_function
This is docstring for second function

Now, if we type help(first_function)

filter_none

edit
close

play_arrow

link
brightness_4
code

print("First Function")
help(first_function)
  
print("\nSecond Function")
help(second_function)

chevron_right


Output:

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



My Personal Notes arrow_drop_up

Check out this Author's contributed articles.

If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

Please Improve this article if you find anything incorrect by clicking on the "Improve Article" button below.


Article Tags :

Be the First to upvote.


Please write to us at contribute@geeksforgeeks.org to report any issue with the above content.