Friday, December 27, 2024
Google search engine
HomeLanguagesPython | functools.wraps() function

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 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_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__)


Output:

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)


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.
 

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_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__)


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

Python3




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


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

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_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__)


Output: 

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)


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

 

RELATED ARTICLES

Most Popular

Recent Comments