Reputation: 28008
I want to construct classes for use as decorators with the following principles intact:
This is for a Django project, and the specific case I am working on now the method needs 2 decorators, and to appear as a normal python function:
@AccessCheck
@AutoTemplate
def view(request, item_id) {}
@AutoTemplate changes the function so that instead of returning a HttpResponse, it just returns a dictionary for use in the context. A RequestContext is used, and the template name is inferred from the method name and module.
@AccessCheck adds additional checks on the user based on the item_id.
I am guessing it's just to get the constructor right and copy the appropriate attributes, but which attributes are these?
The following decorator won't work as I describe:
class NullDecl (object):
def __init__ (self, func):
self.func = func
def __call__ (self, * args):
return self.func (*args)
As demonstrated by the following code:
@NullDecl
@NullDecl
def decorated():
pass
def pure():
pass
# results in set(['func_closure', 'func_dict', '__get__', 'func_name',
# 'func_defaults', '__name__', 'func_code', 'func_doc', 'func_globals'])
print set(dir(pure)) - set(dir(decorated));
Additionally, try and add "print func.name" in the NullDecl constructor, and it will work for the first decorator, but not the second - as name will be missing.
Refined eduffy's answer a bit, and it seems to work pretty well:
class NullDecl (object):
def __init__ (self, func):
self.func = func
for n in set(dir(func)) - set(dir(self)):
setattr(self, n, getattr(func, n))
def __call__ (self, * args):
return self.func (*args)
def __repr__(self):
return self.func
Upvotes: 38
Views: 21422
Reputation: 40272
A do-nothing decorator class would look like this:
class NullDecl (object):
def __init__ (self, func):
self.func = func
for name in set(dir(func)) - set(dir(self)):
setattr(self, name, getattr(func, name))
def __call__ (self, *args):
return self.func (*args)
And then you can apply it normally:
@NullDecl
def myFunc (x,y,z):
return (x+y)/z
Upvotes: 24
Reputation: 100914
To create a decorator that wraps functions in a matter that make them indistinguishable from the original function, use functools.wraps
.
Example:
def mydecorator(func):
@functools.wraps(func):
def _mydecorator(*args, **kwargs):
do_something()
try:
return func(*args, **kwargs)
finally:
clean_up()
return _mydecorator
# ... and with parameters
def mydecorator(param1, param2):
def _mydecorator(func):
@functools.wraps(func)
def __mydecorator(*args, **kwargs):
do_something(param1, param2)
try:
return func(*args, **kwargs)
finally:
clean_up()
return __mydecorator
return _mydecorator
(my personal preference is to create decorators using functions, not classes)
The ordering of decorators is as follows:
@d1
@d2
def func():
pass
# is equivalent to
def func():
pass
func = d1(d2(func))
Upvotes: 8
Reputation: 29885
The decorator module helps you writing signature-preserving decorators.
And the PythonDecoratorLibrary might provide useful examples for decorators.
Upvotes: 10