Introduction To Decorators In Python

Introduction To Decorators In Python

6 mins read517 Views Comment
Updated on Nov 22, 2022 19:34 IST
Recommended online courses

Best-suited Python for data science courses for you

Learn Python for data science with these high-rated online courses

Free
4 weeks
12 K
8 hours
4.24 K
6 weeks
40 K
100 hours
4.99 K
– / –
– / –
– / –
– / –
60 hours
– / –
90 hours
1.26 L
12 hours

What is Python Decorator?

Python decorator is a function that takes a function as an input and returns a new function as an output.

In simple terms, decorators take in a function, add some functionality to the existing function and return it.

It is used to modify the function’s behavior without permanently changing it. 

Mainly, decorators are used for logging, timing, and verifying permission. It can also be used to remove duplication in the program.

Usually, decorators are called before the function you want to decorate.

Must Check: What is Python?

Prerequisites for Decorators

For a clear understanding of decorators in python, we must have to know some concepts that will be used while creating decorators:

  • How to create a function in python?
  • Concept of passing a function as an argument to another function
  • Concept of inner function – Function defined inside another function

Must Check: Functions in Python

Must Check: Python Online Course & Certification

Why do we need Python Decorators?

If there is any common behavior between more than one function or we want to change the parameter (or argument) of any pre-defined functions, we can use the decorator.

Let’s understand the need for the python decorator with a simple example:

Example: Find the factorial.

 
def fact(n):
if n == 0:
return 1
else:
return (n*fact(n-1)) #this is a recursive formula
Copy code

In the above program, we find the factorial for any given value of n.

The above program will be good for every non-negative integer. 

But what if we take the value of n is negative, it will return the error, or it will not compute.

Now, What??

We will use a decorator to overcome this issue.

So, let’s check how to use a decorator in python to fix this issue.

How to create Python Decorators?

  • Define an outer function that takes a function(here fact) as an argument 
  • Create an inner function with the required new features
  • Use @ symbol along with the name of the decorator function

Now, let’s create a decorator for our above example.

 
def factorial_decorator_function(fun):
def inner (n):
print ("factorial of", n, "is")
if n < 0:
print ("Enter positive values only")
return
return fun(n)
return inner
@factorial_decorator_function
def fact(n):
if n == 0:
return 1
else:
return (n*fact(n-1)) #this is a recursive formula
Copy code
2022_05_image-94.jpg

In the above example, factorial_decorator_function is a decorator.

Now we can see that the decorator function added new functionality (i.e. If we input the negative value to find the factorial, it will always return the message “Enter the Positive Values Only”) to the original function.

If we look closely, the parameter of the inner function (inside the decorator) is precisely the same as the parameter of the function.

Array Manipulation Using NumPy
Array Manipulation Using NumPy
NumPy is a foundation library for scientific computations in Python, literally standing for Numerical Python. It contains sophisticated functions and tools for integrating with other programming languages as well. In...read more
Sorting Data using Pandas
Sorting Data using Pandas
In this article, we will discuss how to do sorting in Pandas. Sorting makes it easier to comprehend and analyze data.
Series vs. DataFrame in Pandas – Shiksha Online
Series vs. DataFrame in Pandas – Shiksha Online
In this tutorial, we are going to learn the two most common data structures in Pandas – Series and DataFrame. 

Working with Python Decorators 

Example: 

 
def decorator(func):
  #The wrapper function inside
  #which the argument is called
  def wrapper():
    print("This statement is printed before the function is called")
    func()
    print("This statement is printed after the function is called")
  
  return wrapper
def hello():
  print("Hello, the function is executing")
hello = decorator(hello)
hello()
Copy code

Output: 

2022_08_image-86.jpg

What have we done here? 

We have defined two functions here: 

  • The decorator: This is the decorator function that accepts another function as an argument and decorates aka modifies it before returning. Inside the decorator, we have defined a function called the wrapper Function. This is the function that performs the modification by wrapping the argument passed to it, function func. The decorator returns the wrapper function. 
  • The hello: This is the ordinary function that is to be decorated. We pass the hello function as an argument to the decorator. Consequently, hello now points to the wrapper function returned by the decorator. 

However, the wrapper function has a reference to the original hello function as func, and calls that function between the two calls to print(). 

Syntactic Decorator  

Do you know what syntactic sugar means in computer science lingo? It is syntax within a programming language that is designed to make the code easier to understand by humans. It makes things “sweeter” for our use, hence the name. 

Python offers another way to use decorators by providing syntactic sugar with the @ symbol. 

Syntax:  

@decorator 

def func(arg1, arg2, …): 

    pass 

Consider the previous example but this time, with the syntactic decorator: 

 
def decorator(func):
  #The wrapper function inside
  #which the argument is called
  def wrapper():
    print("This statement is printed before the function is called")
    func()
    print("This statement is printed after the function is called")
  
  return wrapper
@decorator
def hello():
  print("Hello, the function is executing")
hello()
Copy code

Output: 

2022_08_image-88.jpg

Note how we are using @decorator instead of writing hello = decorator(hello)

Decorated Functions with Parameters 

Suppose the function we need to decorate has some parameters. How are decorators used in such cases? 

Let’s understand with the following example: 

 
import functools
def decorator(func):
  @functools.wraps(func)
  
  def wrapper():
    func()
    func()
  
  return wrapper
@decorator
def hello(x):
  print(f"Hello, {x}!")
hello("World")
Copy code

Output: 

2022_08_image-87.jpg

The above code returns a TypeError because the wrapper function has no arguments passed to it. 

Now, we could pass one argument to the wrapper but then we will not be able to use the decorator with a function having more than one argument. 

So instead, we will accept a varying number of arguments in the wrapper and pass those arguments to the original function func. 

Look at the modified code below: 

 
import functools
def decorator(func):
  @functools.wraps(func)
  def wrapper(*args,**kwargs):
    func(*args,**kwargs)
    func(*args,**kwargs)
  
  return wrapper
@decorator
def hello(x):
  print(f"Hello, {x}!")
hello("World")
Copy code

Output: 

2022_08_image-87.jpg

What have we done here? 

We have used *args and **kwargs  to allow us to pass variable number of arguments or keyword arguments to a function. 

Now, we pass ”World” as x to the hello function which is received by the wrapper function to call the actual function func. This is why Hello, World! is displayed twice. 

Decorator Functions with Parameters 

The decorator function itself can have parameters passed to it. This can be done by defining the decorator inside another function that accepts the parameters and then using those parameters inside the decorator. Note that you need to return the decorator from the enclosing function.  

Let’s understand with the following example. In the previous example, we had created a decorator function that will be extended here to repeat any number of times. Let’s name the decorator function as repeat this time. 

 
import functools
def repeat(num):
  def decorator_repeat(func):
    @functools.wraps(func)
    
    def wrapper(*args,**kwargs):
      for _ inrange(num):
        val = func(*args,**kwargs)
      return val
    return wrapper
  
  return decorator_repeat
@repeat(num=3)
def hello(x):
  print(f"Hello, {x}!")
hello("World")
Copy code

Output: 

2022_08_image-88.jpg

What have we done here? 

As you can see from the example above,  

  • The innermost function wrapper is taking a varying number of arguments using *args and **kwargs. It then calls the decorated function num number of times. The wrapper returns the return value of the original decorated function. 
  • Outside the wrapper function, one level above, we have the decorator_repeat function which works as a normal decorator that returns the wrapper function. 
  • At the outermost level, we have the decorator function called repeat that accepts a parameter and provides it to the inner functions using the closure pattern. 

Note that, this time we have used the decorator repeat with a parenthesis ( ) to pass a parameter inside it. 

Chaining Decorators in Python 

Multiple decorators can be chained in Python. By chaining, we mean that one can apply multiple decorators to a single function. The chained decorators are also known as nesting decorators

Let’s understand with the following example: 

 
import functools
def stringSplit(func):
  @functools.wraps(func)
  
  def wrapper(*args,**kwargs):
    return func(*args,**kwargs).split()
  
  return wrapper
def toLower(func):
  @functools.wraps(func)
  
  def wrapper(*args,**kwargs):
    return func(*args,**kwargs).lower()
  
  return wrapper
Copy code

As you can see in the above, code, the first decorator takes a function that returns a string and splits it into a list of words. Then, the second decorator takes a function that returns a string and converts each character into lowercase.  

Now, we will stack both the decorators and use them on a single function as shown below: 

 
@stringSplit
@toLower
def hello(x):
  returnf"Hello {x}"
print(hello("World"))
Copy code

Output: 

2022_08_image-86.jpg

Classes as Decorators 

As mentioned above, even classes in Python can be used as decorators. There are two requirements to make a class as a decorator: 

  • The __init__ function should take a function as an argument. 
  • The class needs to implement the __call__ method (because a decorator must be a callable object, as discussed above).  

Conclusion

In this article, we have discussed in detail about what are python decorator, why we need them and finally how to create python decorators.

Hope this article will help you in your data science journey.

About the Author

This is a collection of insightful articles from domain experts in the fields of Cloud Computing, DevOps, AWS, Data Science, Machine Learning, AI, and Natural Language Processing. The range of topics caters to upski... Read Full Bio