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