Decorator classes in Python

Question:

I want to construct classes for use as decorators with the following principles intact:

  1. It should be possible to stack multiple such class decorators on top off 1 function.
  2. The resulting function name pointer should be indistinguishable from the same function without a decorator, save maybe for just which type/class it is.
  3. Ordering off the decorators should not be relevant unless actually mandated by the decorators. Ie. independent decorators could be applied in any order.

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
Asked By: Staale

||

Answers:

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
Answered By: eduffy

The decorator module helps you writing signature-preserving decorators.

And the PythonDecoratorLibrary might provide useful examples for decorators.

Answered By: f3lix

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))
Answered By: codeape
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.