Codementor Events

Python class decorator - part II - with configuration arguments

Published Feb 04, 2019Last updated Feb 05, 2019
Python class decorator - part II - with configuration arguments

Here we continue to reveal protocol about class based decorator and how it is implemented in python3. You can check and previous article: how class decorator constructs without arguments In most cases we’d like to pass arguments to our decorator in order to setup things required for wrapping. The similarity with those without arguments is that again we need both methods init and call … but now logic slightly changed let’s verify:

class MyClassDecorator:
    def __init__(self, *a, **kw):
        print('__init__',a ,kw)

    def __call__(self, *a, **kw):
        print('__call__',a, kw)


@MyClassDecorator(1,2,3,"decorator configuration")
def my_function(*args, **kwargs):
    print('call my_function', args, kwargs)
    return 3

When we run script it is easy now to understand what moved and where...

Python 3.7.0 (default, Jan 29 2019, 14:54:06)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.2.0 -- An enhanced Interactive Python. Type '?' for help.
__init__ (1, 2, 3, 'decorator configuration') {}
__call__ (<function my_function at 0x7f16f0404ae8>,) {}

You see now arguments are not passing the same way, now first init just takes what is needed for configuration to our wrapping functionality and original function is passed into call

We can use the same methodology like in previous article and put a pseudo goals that will emulate something general about decorator pattern and this does not differ much from previous case, it's just enhance with our custom (and may be default) configuration and parametrization always making things flexible and re-useful.

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

@MyClassDecorator(1001,a='some configuration')
def my_function(*args, **kwargs):
    print('call my_function', args, kwargs)
    return 3

and try it in shell

In [1]: my_function(1,2,3, a="OK")
preprocessing
preprocessing configuration (1001,) {'a': 'some configuration'}
preprocess OK (6, 2, 3)
call my_function (6, 2, 3) {'a': 'OK'}
postprocessing 3
Out[1]: 10

In [2]:

@Cool ?! - Do you see how python interpreter parsing your piece of magic 😃

Discover and read more posts from Dobri Stoilov
get started
post commentsBe the first to share your opinion
Show more replies