Codementor Events

Python Class Decorator - Part I - simple without configuration arguments.

Published Feb 02, 2019Last updated Feb 10, 2019
Python Class Decorator - Part I - simple without configuration arguments.

You may alredy learned decorators in your Python Developer live?! This is quick recipe without a lot of explanantion about internals and it may looks like magic potion that you may not understand at the moment...Don't worry! Believe me, any good magician start without knowing how magic works internaly, but learn to spell. For sure the curious one should turn out the earth to find out how it works!

Great! Python class based decorators are two kinds like function decorators, one that take parameters: and one which simply not.

Let start with simple one without arguments.

1-st we need a target function to wrap my_function and class that will implement decorator wrapper for our function MyClassDecorator, lets code it:

class MyClassDecorator:
  pass
    
    
@MyClassDecorator
def my_function(*args, **kwargs):
    print('my orginal function')

We do not expect much but, lets run it.

$ipython -i class_decorators.py
...
TypeError: MyClassDecorator() takes no arguments

Python interpretator tells us that somehow, at this point "@" something has been put into MyClassDecorator() constructor and our implementation just missing it, let's find out what is passed:

class MyClassDecorator:
    def __init__(self, *args, **kwargs):
        print('__init__', self, args, kwargs)

@MyClassDecorator
def my_function(*args, **kwargs):
    print('my orginal function')

Python class decorator objects expect one initialization argument (plus self). It should be function subject for decorator

__init__ <__main__.MyClassDecorator object at 0x7f0974c25710> (<function my_function at 0x7f0974bf5ae8>,) {}

And now is time to call decorated my_function() with whatever args and kwargs let's see effect now:

$ipython -i class_decorators.py
...
__init__ <__main__.MyClassDecorator object at 0x7f734beb9748> (<function my_function at 0x7f734be8aae8>,) {}
...
In [1]: my_function(1, 2, 3)
..
TypeError: 'MyClassDecorator' object is not callable

Python class decorator objects must be callable (implements call method) that will wrap original function code

let's define some general pseudo goals of our decorator:

  • (1) Preprocess something before original my_function
  • (2) Execute original code of my_function and get the result
  • (3) Postprocess my function result and return post-processed value

class MyClassDecorator:
    def __init__(self, func):
        self.func = func
       
    def __call__(self, *args, **kwargs):
        print('preprocessing', args)
        if args:
            if isinstance(args[0], int):
                a = list(args)
                a[0] += 5
                args = tuple(a)
                print('preprocess OK', args) 
        r = self.func(*args, **kwargs)
        print('postprocessing', r)
        r += 7
        return r
        

@MyClassDecorator
def my_function(*args, **kwargs):
    print('call my_function', args, kwargs)
    return 3

In resume:

Recipe to implement simple python class based decorator:

  1. __init__(self, func ) - func argument will be referenced to decorated function, Create in init body object member variable-function self.func = func - you will need it for goal (2).

  2. __call__(self, *args, **kwargs) - which is actually wrapper code for your decorated function, implement goal: (1), (2), (3) in same order.

Next: python class decorator with arguments

Discover and read more posts from Dobri Stoilov
get started
post commentsBe the first to share your opinion
Justin “Jab”
5 years ago

Great stuff! Just typos everywhere, please review kept getting caught up on them while reading.

Dobri Stoilov
5 years ago

Justin, thanks a lot! I’m trying to improve style, spelling and grammar. Any help appreciated!

Jakob Guldberg Aaes
5 years ago

Grammarly in conjunction with language tool is an invaluable combination for me :)

Clemens Nyffeler
5 years ago

It’s actually charming. I can almost hear your slavic accent when reading the article, which is very informative btw.
If you want to improve, one thing you can do is use articles more (a & the).

Show more replies