Attaching a decorator to all functions within a class

Following code works for python2.x and 3.x

import inspect

def decorator_for_func(orig_func):
    def decorator(*args, **kwargs):
         print("Decorating wrapper called for method %s" % orig_func.__name__)
         result = orig_func(*args, **kwargs)
         return result
    return decorator

def decorator_for_class(cls):
    for name, method in inspect.getmembers(cls):
        if (not inspect.ismethod(method) and not inspect.isfunction(method)) or inspect.isbuiltin(method):
            continue
        print("Decorating function %s" % name)
        setattr(cls, name, decorator_for_func(method))
    return cls

@decorator_for_class
class decorated_class:
     def method1(self, arg, **kwargs):
         print("Method 1 called with arg %s" % arg)
     def method2(self, arg):
         print("Method 2 called with arg %s" % arg)


d=decorated_class()
d.method1(1, a=10)
d.method2(2)

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 using inspect:

import inspect

class Something:
    def foo(self): 
        pass

for name, fn in inspect.getmembers(Something, inspect.isfunction):
    setattr(Something, name, decorator(fn))

In practice of course you'll want to apply your decorator more selectively. 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.


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
         result = func(*args, **kwargs)
         print "after",func.func_name
         return result
      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