Functools Wraps Made Simple: What You Need to Know

In this article, we will look at an important method named wraps of functools module of python. You might have worked with or come across a term decorator. Decorators are the higher-order functions that increase the functionality of first-order functions without changing their functioning. To put it differently, higher-order functions take in and return a function, as an argument and output as a result respectively. Read more about decorators here.

Although decorators might pump up your regular functions, however, they present a challenge which is, not preserving the information/signature of the original function. We will try to overcome this problem and provide a solution in the following article using the wraps method.

Functools.wraps()

The wraps function is a part of the functools module of Python. It wraps and updates the wrapper function of the decorator by copying the attributes such as _name__, __doc__, the docstring etc. It preserves the signature o

Moreover, the functools library has many other useful functions to deal with higher-order functions, we will be focussing on the wraps method. You can check out the rest of them from here.

Importing functools wraps

# use as @functools.wraps()
import functools

# use directly as @wraps()
from functools import wraps

Syntax

Syntax: @functools.wraps(wrapped, assigned = WRAPPER_ASSIGNMENTS, updated = WRAPPER_UPDATES)

To use it, simply wrap the wrapper function as:

Using functool.wraps() to wrap the wrapper function
Using functool.wraps() to wrap the wrapper function

Paramters

Let’s look at the parameters of the functools wraps decorator:

  • Wrapped: a function passed as a argument in decorator.
  • Assigned: set to WRAPPER_ASSIGNMENTS (which assigns to the wrapper function’s __module__, __name__, __qualname__, __annotations__ and __doc__, the documentation string).
  • Updated: By default it is set to WRAPPER_UPDATES (which updates the wrapper function’s __dict__, i.e. the instance dictionary). 

Problem with decorators

For the sake of easy understanding, let’s take this example.

def my_decorator(original_func):
	def wrapper(*args, **kwargs):
		"""this is wrapper documentation...."""
		original_func(*args,**kwargs)
	return wrapper

def greet_hindi(name:str):
	"""Greets in english"""
	print(f'Namaste, {name}!')

@my_decorator
def greet_french(name:str):
	"""Greets in french"""
	print(f'Bonjour, {name}!')

greet_hindi('Philip')
greet_french('Rohan')
A decorator example
The output of greet_hindi & greet_english functions
print(greet_hindi.__name__,greet_hindi.__doc__)
print(greet_french.__name__,greet_french.__doc__)
unexpected results
The information of greet_hindi & greet_french function

If you look at the image above; names and documentations of functions greet_hindi and greet_french aren’t printed. Instead, the name and documentation of the wrapper function of my_decorator got printed.

What is causing it? If you take a glance at my_decorator; it returns the wrapper function, which is responsible for this. With this in mind, decorators do not preserve the information or details of the functions(first-order functions) passed into them as arguments. Therefore, this problem needs to be dealt with.

Solution to the problem

A straightforward solution is to use the functools.wraps(). Hence, use it to decorate the wrapper function of the decorator. In other words, we need to put it above the wrapper function. The ‘@’ symbol, like shown below, and the first-class function passed as an argument.

def my_decorator(original_func):
    @functools.wraps(orginal_func)
	def wrapper(*args, **kwargs):
		"""this is wrapper documentation...."""
		original_func(*args,**kwargs)
	return wrapper

If we try to print the names and documentation again, we won’t be facing the problem of the decorator not preserving the information of the passed function.

using functools.wraps()
Using wraps decorator fixes the issue.

functools wraps vs decorators method

functools.wraps()decorator method
A built-in function of the functools moduleNot a part of Python’s standard library has to be installed
It appears to preserve the signature/information of the function passedTruly preserves the signature/information of function passed
Use from functools import wraps to use it codeFirst, pip install decorator & then import decorator

The only caveat is that you have to install it using pip. It increases the dependencies while functools wraps is a built-in method and therefore is a better alternative.

FAQs on Functools Wraps

What does Functools wrap do?

Functools wraps method wraps the wrapper function of the decorator and copies the attributes such as _name__, __doc__ (the docstring), etc of the passed function in the decorator. As a result, it preserves passed function information.

What are functools?

Functools is a library of Python used to work with higher-order functions like decorators. Functools library has decorators/functions like wraps, partial, total_ordering, etc.

Can we preserve signature using functools wraps?

Yes, you can preserve the signature using the functools wraps method.
For instance,
def multiply(num1,num2):
"""Multiplies two numbers"""
mul = num1 * num2
print('Multiplication of {} {} is '.format(num1,num2,mul))
print(help(multiply))


Functool Wraps

Conclusion

In this article, we looked at an important problem relating to decorators. Decorators can increase the functionings of first-class functions without making changes to them. However, they too can fall short in some places, for example, failing to preserve the information about the original function. But likewise, wraps can easily remove those shortcomings. We also covered the decorator’s method to preserve the function’s information.

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments