views:

55

answers:

3

I don't really need to do this, but was just wondering, is there a way to bind a decorator to all functions within a class generically, rather than explicitly stating it for every function.

I suppose it then becomes a kind of aspect, rather than a decorator and it does feel a bit odd, but was thinking for something like timing or auth it'd be pretty neat.

A: 

You could override the __getattr__ method. It's not actually attaching a decorator, but it lets you return a decorated method. You'd probably want to do something like this:

class Eggs(object):
    def __getattr__(self, attr):
        return decorate(getattr(self, `_` + attr))

There's some ugly recursion hiding in there that you'll want to protect against, but that's a start.

Nathon
+2  A: 

The cleanest way to do this, or to do other modifications to a class definition, is to define a metaclass.

Alternatively, just apply your decorator at the end of the class definition:

class Something:
   def foo(self): pass

for name, fn in inspect.getmembers(Something):
    if isinstance(fn, types.UnboundMethodType):
        setattr(Something, name, decorator(fn))

For Python 3 replace types.UnboundMethodType with types.FunctionType.

In practice of course you'll want to apply your decorator more selectively, and as soon as you want to decorate all but one method you'll discover that it is easier and more flexible just to use the decorator syntax in the traditional way.

Duncan
I totally agree, I'm also much more comfortable with the explicit decorating of every function rather than a magic implicit decorator.Was just curious is all.
dochead
A: 

Everytime you think of changing class definition, you can either use the class decorator or metaclass. e.g. using metaclass

import types

class DecoMeta(type):
   def __new__(cls, name, bases, attrs):

      for attr_name, attr_value in attrs.iteritems():
         if isinstance(attr_value, types.FunctionType):
            attrs[attr_name] = cls.deco(attr_value)

      return super(DecoMeta, cls).__new__(cls, name, bases, attrs)

   @classmethod
   def deco(cls, func):
      def wrapper(*args, **kwargs):
         print "before",func.func_name
         func(*args, **kwargs)
         print "after",func.func_name
      return wrapper

class MyKlass(object):
   __metaclass__ = DecoMeta

   def func1(self): 
      pass

MyKlass().func1()

Output:

before func1
after func1

Note: it will not decorate staticmethod and classmethod

Anurag Uniyal