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