Class based decorators in python

Suresh Kumar - Apr 19 '22 - - Dev Community

In the previous articles of Python decorator series we learnt to create a simple function based decorator, how it works under the hood and how to fix the doc strings.

In this article, we will learn to create a class based decorator and use it to decorate functions.


class HelloDecorator:
    """Simple class decorator"""

    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        """Simple class call method"""
        print(f'Calling {self.func.__name__}')
        result = self.func(*args, **kwargs)
        return result


@HelloDecorator
def add(a, b):
    """Simple function that returns sum of two numbers"""
    return a + b


if __name__ == '__main__':
    output1 = add(2, 2)
    print('Result:: ', output1)

Enter fullscreen mode Exit fullscreen mode
Calling add
Result::  4
Enter fullscreen mode Exit fullscreen mode

OK, We got the decorator working. Lets look at the help and name and doc attributes,

help(add) # --> prints class definition
print(add.__doc__) # --> prints Simple class decorator
print(add.__name__) # --> Raises AttributeError
Enter fullscreen mode Exit fullscreen mode

Let's try to fix the doc strings,
use funct_tools.partial to define get magic method and then funct_tools.update_wrapper to let python know the wrapped function.

from functools import update_wrapper, partial


class HelloDecorator:
    """Simple class decorator"""

    def __init__(self, func):
        self.func = func
        # fixes __name__ and __doc__ attributes
        update_wrapper(self, func)

    def __get__(self, obj):
        """Fixes help description"""
        return partial(self, obj)

    def __call__(self, *args, **kwargs):
        """Simple class call method"""
        print(f'Calling {self.func.__name__}')
        result = self.func(*args, **kwargs)
        return result
Enter fullscreen mode Exit fullscreen mode

now lets check the docs,

help(add) # --> prints
"""
add(a, b)
    Simple function that returns sum of two numbers
"""
print(add.__doc__) # --> prints "add"
print(add.__name__) # --> prints "Simple function that returns sum of two numbers"
Enter fullscreen mode Exit fullscreen mode

Conclusion, while some may class based decorators prefer, I personally do not prefer class based decorator because of the extra boilerplate code to fix the doc strings.

In the next article, we will implement various kinds decorator recipes. Stay tuned for upcoming articles. Connect with me on twitter to get my future articles.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .