This is meant to be a method for writing decorators very easily.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | class AttributeHelper(object):
def __new__(typ, *attr_args, **attr_kwargs):
self = object.__new__(typ)
def f2(orig_func):
def f3(*func_args, **func_kwargs):
# Take argument list for original function
return self(*func_args, **func_kwargs)
f3.func_name = orig_func.func_name
self.orig_func = orig_func
self.new_func = f3
self.__init__(*attr_args, **attr_kwargs)
return f3
return f2
def __init__(self, *attr_args, **attr_kwargs):
return self.on_init(*attr_args, **attr_kwargs)
def __call__(self, *args, **kwargs):
return self.on_call(*args, **kwargs)
def on_call(self, *args, **kwargs):
return self.orig_func(*args, **kwargs)
##### Example
class DebugAttribute(AttributeHelper):
def on_init(self, msg):
self.msg = msg
self.log = []
self.new_func.print_log = self.print_log
def on_call(self, *args, **kwargs):
result = super(DebugAttribute, self).on_call(*args, **kwargs)
self.log.append((args, kwargs,result))
return result
def print_log(self):
name = self.orig_func.func_name
for args, kwargs, result in self.log:
join_str = ', '
if args==():
args_str = ''
join_str=''
else:
args_str = ', '.join(str(i) for i in args)
if kwargs=={}:
kwargs_str = ''
join_str=''
else:
kwargs_str = ', '.join('%s=%s'%(str(k),str(v)) for k,v in kwargs.items())
result_str = str(result)
print '%s(%s%s%s) -> %s'%(name, args_str, join_str, kwargs_str, result_str)
if __name__=='__main__':
@DebugAttribute('hi')
def hi(a,b):
return a+b
hi(1,2)
hi(4,5)
hi(a=6,b=9)
hi.print_log()
|
One of the major problems with decorators is that you can easily end up with up to 3 function declerations: 1) Getting the decorator arguments 2) Getting the original function 3) The actual call to the function
This class makes it much simpler and should allow for less hassle in writing decorators. Notice that even if you don't require any arguments for your attribute you will still need to use: @attr() def f(): pass
instead of: @attr def f(): pass
This class can definatly be improved in many ways: * Make the outer class look more like the original function (argument list, filename, docstring, etc...) * Automatically detect if on_init has any arguments and then do not require @attr() * Any other suggestions would be gladly added
Nice idea! Haven't tried the recipe yet, but compacting the 3-function idiom sounds useful.
This is overly complex. Try this: