sureshdsk.dev

sureshdsk.dev

Class based decorators in python

Class based decorators in python

Subscribe to my newsletter and never miss my upcoming articles

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)
Calling add
Result::  4

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

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

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"

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.

Interested in reading more such articles from Suresh Kumar?

Support the author by donating an amount of your choice.

 
Share this