You might be knowing how to create decorators in python using functions. In this article, we will discuss ways to create decorators using classes in Python. First we will discuss what are callable objects and then we will implement decorators using those callable objects by defining classes for them.
What are Callable Objects?
Any object in python that can be called just like functions and methods is called a callable object. In python every callable object has an implementation of the __call__() method in its class definition. We can say that any object that has the __call__() method in its class definition is called a callable object.
To call an object, we need to implement the __call__() method in the class definition of the object.
For example, look at the following source code.
class Car:
def __init__(self):
self.brand = "Tesla"
self.speed = "100mph"
def __call__(self, *args, **kwargs):
print("Called me? I am coming at {} speed.".format(self.speed))
Here, we have defined a class named Car. Apart from its constructor definition, we have also defined the __call__() method in its class definition. Now we can call any instance of the Car class and the __call__() method will be executed as follows.
class Car:
def __init__(self):
self.brand = "Tesla"
self.speed = "100mph"
def __call__(self, *args, **kwargs):
print("Called me? I am coming at {} speed.".format(self.speed))
# create callable object
myCar = Car()
# call myCar
myCar()
Output:
Called me? I am coming at 100mph speed.
We can use any callable object that accepts a function or a callable object as input and returns a callable object or a function to implement python decorators.
How to create decorators using classes?
We can create decorators using classes by defining callable objects using the __call__() method, which we will discuss in this section.
First, we will define a function add() that takes two numbers as input and prints their sum.
def add(num1, num2):
value = num1 + num2
print("The sum of {} and {} is {}.".format(num1, num2, value))
# execute
add(10, 20)
Output:
The sum of 10 and 20 is 30.
Now, we have to define a decorator in such a way that the add() function should also print the product of the numbers along with the sum. For this, we can create a decorator class.
We can implement the __call__() method inside a class to implement the decorators.
First, we will define the constructor of the decorator_class that accepts the add() function as an input parameter and assigns it to a class variable func.
Then we will implement the __call__() method. The code inside the __call__() method will calculate the product of the input numbers and will print it. After that, it will call the input function add() with the given input numbers. At last, it will return the value returned by the add() function.
class decorator_class:
def __init__(self, func):
self.func = func
def __call__(self, *args):
product = args[0] * args[1]
print("Product of {} and {} is {} ".format(args[0], args[1], product))
return self.func(args[0], args[1])
Inside the __call__() method, we have implemented the code that prints the product of the numbers that are given as input and then returns the output of the add() function after calling it. The add() function prints the sum of the input numbers. As we have defined the function and the decorator class, let us see how we can use them.
Create Decorators by passing functions as arguments to the class constructor
To create a decorated function, We can pass the add() function as an input argument to the constructor of the decorator class. The add() function will be assigned to the func variable in the decorator_class. Once an instance of the decorator_class is called, it will execute the code inside the __call__() method to print the product of the input numbers. Then it will return the output of the function func after calling it. As the add() function has been assigned to func, it will print the sum of the numbers.
class decorator_class:
def __init__(self, func):
self.func = func
def __call__(self, *args):
product = args[0] * args[1]
print("Product of {} and {} is {} ".format(args[0], args[1], product))
return self.func(args[0], args[1])
def add(num1, num2):
value = num1 + num2
print("The sum of {} and {} is {}.".format(num1, num2, value))
# execute
decorator_object = decorator_class(add)
decorator_object(10, 20)
Output:
Product of 10 and 20 is 200
The sum of 10 and 20 is 30.
Create decorators by using @ sign
Instead of passing the add function to the class constructor, we can specify decorator_class name with @ sign before the definition of the add() function. After this, whenever the add() function is called, It will always print both the product and sum of the input numbers.
class decorator_class:
def __init__(self, func):
self.func = func
def __call__(self, *args):
product = args[0] * args[1]
print("Product of {} and {} is {} ".format(args[0], args[1], product))
return self.func(args[0], args[1])
@decorator_class
def add(num1, num2):
value = num1 + num2
print("The sum of {} and {} is {}.".format(num1, num2, value))
# execute
add(10, 20)
Output:
Product of 10 and 20 is 200
The sum of 10 and 20 is 30.
In this method, there is a drawback that you cannot use the add() function to just add the numbers while defining decorated functions using @ sign. It will always print the product of the numbers along with their sum. So,If you are using the add() function at any other place in its original form, it will cause an error.
Conclusion
In this article, we have discussed ways to create python decorators using classes in Python. To learn more about python programming, you can read this article on list comprehension. You may also like this article on the linked list in Python.
Recommended Python Training
Course: Python 3 For Beginners
Over 15 hours of video content with guided instruction for beginners. Learn how to create real world applications and master the basics.