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):
func()
return wrapper
@a_decorator
def first_function():
print ( "first function" )
@a_decorator
def second_function(a):
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):
func()
wrapper.__name__ = func.__name__
wrapper.__doc__ = func.__doc__
return wrapper
@a_decorator
def first_function():
print ( "first function" )
@a_decorator
def second_function(a):
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):
func()
return wrapper
@a_decorator
def first_function():
print ( "first function" )
@a_decorator
def second_function(a):
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
Whether you're preparing for your first job interview or aiming to upskill in this ever-evolving tech landscape,
GeeksforGeeks Courses are your key to success. We provide top-quality content at affordable prices, all geared towards accelerating your growth in a time-bound manner. Join the millions we've already empowered, and we're here to do the same for you. Don't miss out -
check it out now!