Decorating Classes

The above section shows how decorators help in decorating functions. We have seen that decorators are callable that accepts and returns a callable. Since classes are callable,  decorators are also used to decorate classes. Some of the uses of decorating classes are:

  • Addition or enhancement of attributes
  • Attribute interaction
  • Alter the API of a class (alter the way of declaring classes and its instance use)

Let’s go through how to decorate a function using class.




class MyDecorator:
  
    def __init__(self, function):
        self.function = function
  
    def __call__(self):
        print("Inside Function Call")
        self.function()
  
@MyDecorator
def function():
    print("w3wiki")
  
def main():
    function()
  
if __name__ == "__main__":
    main()


Output:

Inside Function Call
w3wiki

Let’s look into an another example. Below code, sort the instance based on their creation time. Here we require three additional attributes- the instantiation timestamp, __lt__ and __gt__ methods.




import functools
import time
  
def sort_by_creation_time(cl):
    org_init = cl.__init__
  
    # Enhance the class to store the creation
    # time based on the instantiation.
    @functools.wraps(org_init)
    def new_init(self, *args, **kwargs):
        org_init(self, *args, **kwargs)
        self._created = time.time()
  
    # __lt__ and __gt__ methods return true or false 
    # based on the creation time.
    cl.__init__ = new_init
    cl.__lt = lambda self, other: self._created < other._created
    cl.__gt__ = lambda self, other: self._created > other._created
    return cl
  
@sort_by_creation_time
class Sort(object):
    def __init__(self, identifier):
        self.identifier = identifier
  
    def __repr__(self):
        return self.identifier
  
  
def main():
  
    first = Sort('Python')
    second = Sort('Data Analysis')
    third = Sort('Machine Learning')
  
    sortables = [third, first, second]
    print(sorted(sortables))
  
if __name__ == "__main__":
    main()


Output

[Python, Data Analysis, Machine Learning]

new_init, its primary responsibility is to run the wrapped function and also add an extra functionality to the wrapped function. The @functools.wraps(org_init) update the wrapper function to reflect the look of wrapped function. Check functools for detail understanding.




@functools.wraps(org_init)
def new_init(self, *args, **kwargs):
 
    # calls the init method of class
    org_init(self, *args, **kwargs)
 
    # add an extra attribute and store 
    # creation time  
    self._created = time.time() 
 
# reference of new_init is assigned to
# __init__ method and executes when
# callable creates the class object.
cl.__init__ = new_init  


Python Decorators: A Complete Guide

A decorator is a design pattern tool in Python for wrapping code around functions or classes (defined blocks). This design pattern allows a programmer to add new functionality to existing functions or classes without modifying the existing structure. The section provides an overview of what decorators are, how to decorate functions and classes, and what problem can it solve.

Decorators

Similar Reads

Understanding Decorators

A decorator is a function that takes another function as an argument, does some actions, and then returns the argument based on the actions performed. Since functions are first-class object in Python, they can be passed as arguments to another functions. Hence we can say that a decorator is a callable that accepts and returns a callable. Below code shows a simple decorator that add additional notes to the my_function docstring:...

Decorating Classes

The above section shows how decorators help in decorating functions. We have seen that decorators are callable that accepts and returns a callable. Since classes are callable,  decorators are also used to decorate classes. Some of the uses of decorating classes are:...

Decorate a Function and return a Class

Sometimes it’s necessary to decorate a function and return a class. Say, for example, advanced cases developers can subclass a class in an API. It can also avoid an increase in boilerplate code....

Summary

Decorators are an excellent tool for wrapping code around functions and classes, which provides an efficient way to use boilerplate code and also contributes to readability. Even though they are modular and explicit, there are drawbacks to this tool. Since it’s a tool for wrapping code around defined blocks, debugging becomes more challenging. And also, poorly written decorators may result in an error. After all, a well-written decorator can be treated as an efficient reusable piece of Python functionality for wrapping defined blocks of code....