Reputation: 1171
I have done some research about decorators, mainly about typical use-cases. It turned out that in majority of cases, they act as validators, timers, permission checkers - pretty transparent stuff. I was wondering if it would be pythonic to use them to precalculate and supply some variables to decorated function.
I've on my mind situation like this:
Decorator:
def foo(fn):
@functools.wraps(fn)
def wrapper(*args, **kwargs):
fn_name = fn.__name__
var = Test._do_something(Test.store_dict[fn_name])
kw_args = {**kwargs, **dict(var=var)}
return fn(*args, **kw_args)
return wrapper
Class:
class Test:
store_dict = dict(fn1='fn1_operator',
fn2='fn2_operator')
@staticmethod
@foo
def fn1(dummy_data, **kwargs):
return Test.__handle_data(dummy_data=dummy_data,
**kwargs)
@staticmethod
@foo
def fn2(dummy_data, **kwargs):
return Test.__handle_data(dummy_data=dummy_data,
**kwargs)
@staticmethod
def _do_something(var):
return var
@staticmethod
def __handle_data(dummy_data, var):
return (dummy_data, var)
Usage:
test_instance = Test()
test_instance.fn1(dummy_data='test')
As you can see fn1
and fn2
methods are doing almost same thing (calling __handle_data
method), but with different var
. Variable var
depends on called function name. Calling fn1
results with var=fn1_operator
and so on. Having in my mind the Zen of Python:
Simple is better than complex.
I'm having doubts about that being pythonic.
In the other hand, without that decorator, my code would have a lot of repetitions:
@staticmethod
def fn1(dummy_data):
var = _do_something('fn1')
return Test.__handle_data(dummy_data=dummy_data,
var=var)
@staticmethod
def fn2(dummy_data):
var = _do_something('fn2')
return Test.__handle_data(dummy_data=dummy_data,
var=var)
Is it correct? What should I change?
Upvotes: 2
Views: 62
Reputation: 530960
I wouldn't use a decorator here; I'd write a function that returns a closure over the specific data instead.
def make_test(arg):
@staticmethod
def _(dummy_data):
var = _do_something(arg)
return Test.__handle_data(dummy_data, var=var)
return _
fn1 = make_test('fn1')
fn2 = make_test('fn2')
(I didn't test, but I'm pretty sure it works the same if you decorate the closure as shown, or simply return an undecorated function and write fn1 = staticmethod(make_test('fn1'))
, etc.)
With a decorator that focuses on the what is common, it might look like
def foo(f):
def _(dummy_data):
var = _do_something(f())
return Test.__handle_data(dummy_data, var=var)
return _
@foo
def fn1(self):
return 'fn1'
@foo
def fn2(self):
return 'fn2'
which has the same behavior, but is less clear as to the intent. Too much is hidden in the decorator.
Upvotes: 2