I want to modify the way several of my functions behave, so the use of a decorator comes to mind. For example let's say I have a batch data taking function takeData(s)
:
def takeData(s):
takeDataSet_1(s)
takeDataSet_2(s)
.
.
.
A simple thing that I might want to do is update the parameter dict s, before each of the takeDataSet function calls. So that the effective code would be more like this:
def takeData(s):
s = updateParams(s)
takeDataSet_1(s)
s = updateParams(s)
takeDataSet_2(s)
s = updateParams(s)
.
.
.
Is there a way to do this with a decorator so that my code would look more like
@takeDataWithUpdatedParams
def takeData(s):
takeDataSet_1(s)
takeDataSet_2(s)
Is there a way to control the depth of recursion such a decorator? So that if takeDataSet_1(s) had subroutines of its own s could be updated between them, as in:
@recursiveUpdateParams
def takeData(s):
takeDataSet_1(s)
takeDataSet_2(s)
def takeDataSet_1(s):
takeData_a(s)
takeData_b(s)
Gets executed as
def takeData(s):
s = updateParams(s)
takeDataSet_1(s)
s = updateParams(s)
takeDataSet_2(s)
s = updateParams(s)
def takeDataSet_1(s):
s = updateParams(s)
takeData_a(s)
s = updateParams(s)
takeData_b(s)
Very interesting question. To achieve this, you need to dive deep into the function object (without eval
, exec
or ast
stuff, anyway).
def create_closure(objs):
creat_cell = lambda x: (lambda: x).__closure__[0]
return tuple(create_cell(obj) for obj in objs)
def hijack(mapper):
from types import FunctionType
def decorator(f):
globals_ = {k: mapper(v) for k, v in f.__globals__.items()}
closure_ = f.__closure__
if closure_:
closure_ = create_closure(i.cell_contents for i in closure_)
return (lambda *arg, **kwarg:
FunctionType(f.__code__, globals_, f.__name__,
f.__defaults__, closure_)(*arg, **kwarg))
return decorator
Test:
x = 'x'
y = 'y'
@hijack(lambda obj: 'hijacked!' if obj is x else obj)
def f():
return (x, y)
f()
Out:
('hijacked!', 'y')
Finally a solution to the original problem:
x = lambda: 'x()'
y = lambda: 'y()'
mydecorator = lambda f: lambda *arg, **kwarg: f(*arg, **kwarg) + ' decorated!'
targets = {id(x): mydecorator(x)}
def mapper(obj):
if id(obj) in targets:
return targets[id(obj)]
else:
return obj
@hijack(mapper)
def f():
return (x(), y())
f()
Out:
('x() decorated!', 'y()')