Python Decorators
In Python, a decorator is any callable Python object used to modify a class or function. It takes a function, adds some functionality to it, and returns it. Decorators are primarily useful when you want to extend any existing function without any modification to the original function. Decorators are commonly used to add functionality to existing functions, such as logging, timing, or authentication.
- Decorators are called before the function call.
- It extends the functionality of our original functions without altering their source code.
- Decorators can be of two types:
- Function Decorators
- Class Decorators
Python Function Decorator Example
Function-based decorators are the easiest way to implement decorators in Python. A function-based decorator takes a function as an argument and returns a new function that wraps the original function.
Let’s create a decorator. First, we create one outer function and another inner function inside it. Here we are creating sample_ecorator
the outer function and example_function
as the inner function.
This outer function returns the inner function at the end of the outer function and inside the inner function, we will add our functionality before and after a function
(that we want to extend or add new functionality, in our case that function is the cube(num)
).
Basically, a python decorator takes a function as input and adds some functionality before and after, and returns it back.
# Create a decorator
def sample_decorator(func):
# define the inner function
def example_function(number):
print("Before Funciton Call:", func.__name__)
# call original function
results = func(number)
print("Cube: ", results)
print("After Funciton Call:", func.__name__)
# Return inner function
return example_function
def cube(num):
return num**3
# decorate the function
decorated_func = sample_decorator(cube)
# call the decorated function
decorated_func(2)
Output: Before Funciton Call: cube 8 After Funciton Call: cube
Let’s do it in a more compact way. Instead of assigning the function call to a variable, python has the functionality to do this in a simpler and more attractive way by using @ symbol above the function definition with the decorator name.
def sample_decorator(func):
def example_function(number):
print("Before Funciton Call:", func.__name__)
results = func(number)
print(results)
print("After Funciton Call:", func.__name__)
return example_function
@sample_decorator
def cube(num):
return num**3
cube(2)
Output: Before Funciton Call: cube 8 After Funciton Call: cube In this example,sample_decorator
is the name of the decorator function. When the code is executed, the functioncube
is passed as an argument tosample_decorator
, and the decorated function is then called.
Python Class Decorator Example
Decorators have the capability to modify the behavior of any python function or class. Class-based decorators are a more advanced way to implement decorators in Python. In this section, we will define decorator as a class. For defining this, we will use a __call__() method and pass the function to __init__ as an argument. It allows a class instance to behave like a function. For example, consider the following code example that implements a cube decorator using a class:
class CubeDecorator:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
# before function
print("Before Funciton Call:", self.func.__name__)
# call original function
cube_value = self.func(*args, **kwargs)
print("Cube:", cube_value)
# after function
print("After Funciton Call:", self.func.__name__)
return cube_value
# Add class decorator
@CubeDecorator
def cube(num):
return num**3
cube(20)
Output: Before Funciton Call: cube Cube: 8000 After Funciton Call: cube
After decoration, the call method of the class is called instead of the cube() method.
Real-life Decorator Example
Let’s see one real-life decorator example to calculate the execution time of a function. For example, consider the following code example that implements a execution_time
decorator:
from time import time, sleep
# Create a decorator
def execution_time(func):
# define the inner function
def wrapper(*args, **kwargs):
# get current time before execution
t = time()
# call function
func(*args, **kwargs)
# compute time difference
print(func.__name__, ' function execution time: ', time() - t)
return wrapper
@execution_time
def cube(num):
return num**3
cube(3)
Output: cube function execution time: 2.1457672119140625e-06
The wrapper function of the execution_time decorator calculates the time difference between the before and after the function execution and prints the execution time of the function on the console using the time module.
Summary
Python decorators are the most useful functionality that allows programmers to expand the functionality of a function, class, or method by wrapping it with another function. Decorators mostly used real-life applications such as logging, testing, timing, and authentication. Python has the functionality to add decorators in a simpler and more attractive way by using @ symbol above the function definition with the decorator name.
We can implement decorators using either a function or a class. Function-based decorators take a function as an argument and return a new function that wraps the original function, while class-based decorators use the __call__ method to allow a class instance to behave like a function.