How to wrap every method of a class? [duplicate] How to wrap every method of a class? [duplicate] python python

How to wrap every method of a class? [duplicate]


An elegant way to do it is described in Michael Foord's Voidspace blog in an entry about what metaclasses are and how to use them in the section titled A Method Decorating Metaclass. Simplifying it slightly and applying it to your situation resulted in this:

from functools import wrapsfrom types import FunctionTypedef wrapper(method):    @wraps(method)    def wrapped(*args, **kwargs):    #   ... <do something to/with "method" or the result of calling it>    return wrappedclass MetaClass(type):    def __new__(meta, classname, bases, classDict):        newClassDict = {}        for attributeName, attribute in classDict.items():            if isinstance(attribute, FunctionType):                # replace it with a wrapped version                attribute = wrapper(attribute)            newClassDict[attributeName] = attribute        return type.__new__(meta, classname, bases, newClassDict)class MyClass(object):    __metaclass__ = MetaClass  # wrap all the methods    def method1(self, ...):        # ...etc ...

In Python, function/method decorators are just function wrappers plus some syntactic sugar to make using them easy (and prettier).

Python 3 Compatibility Update

The previous code uses Python 2.x metaclass syntax which would need to be translated in order to be used in Python 3.x, however it would then no longer work in the previous version. This means it would need to use:

class MyClass(metaclass=MetaClass)  # apply method-wrapping metaclass    ...

instead of:

class MyClass(object):    __metaclass__ = MetaClass  # wrap all the methods    ...

If desired, it's possible to write code which is compatible with both Python 2.x and 3.x, but doing so requires using a slightly more complicated technique which dynamically creates a new base class that inherits the desired metaclass, thereby avoiding errors due to the syntax differences between the two versions of Python. This is basically what Benjamin Peterson's six module's with_metaclass() function does.

from types import FunctionTypefrom functools import wrapsdef wrapper(method):    @wraps(method)    def wrapped(*args, **kwargs):        print('{!r} executing'.format(method.__name__))        return method(*args, **kwargs)    return wrappedclass MetaClass(type):    def __new__(meta, classname, bases, classDict):        newClassDict = {}        for attributeName, attribute in classDict.items():            if isinstance(attribute, FunctionType):                # replace it with a wrapped version                attribute = wrapper(attribute)            newClassDict[attributeName] = attribute        return type.__new__(meta, classname, bases, newClassDict)def with_metaclass(meta):    """ Create an empty class with the supplied bases and metaclass. """    return type.__new__(meta, "TempBaseClass", (object,), {})if __name__ == '__main__':    # Inherit metaclass from a dynamically-created base class.    class MyClass(with_metaclass(MetaClass)):        @staticmethod        def a_static_method():            pass        @classmethod        def a_class_method(cls):            pass        def a_method(self):            pass    instance = MyClass()    instance.a_static_method()  # Not decorated.    instance.a_class_method()   # Not decorated.    instance.a_method()         # -> 'a_method' executing


You mean programatically set a wrapper to methods of a class?? Well, this is probably a really bad practice, but here's how you may do it:

def wrap_methods( cls, wrapper ):    for key, value in cls.__dict__.items( ):        if hasattr( value, '__call__' ):            setattr( cls, key, wrapper( value ) )

If you have class, for example

class Test( ):    def fire( self ):        return True    def fire2( self ):        return True

and a wrapper

def wrapper( fn ):    def result( *args, **kwargs ):        print 'TEST'        return fn( *args, **kwargs )    return result

then calling

wrap_methods( Test, wrapper )

will apply wrapper to all methods defined in class Test. Use with caution! Actually, don't use it at all!


If extensively modifying default class behavior is the requirement, MetaClasses are the way to go. Here's an alternative approach.

If your use case is limited to just wrapping instance methods of a class, you could try overriding the __getattribute__ magic method.

from functools import wrapsdef wrapper(func):    @wraps(func)    def wrapped(*args, **kwargs):        print "Inside Wrapper. calling method %s now..."%(func.__name__)        return func(*args, **kwargs)    return wrapped

Make sure to use functools.wraps while creating wrappers, even more so if the wrapper is meant for debugging since it provides sensible TraceBacks.

import typesclass MyClass(object): # works only for new-style classes    def method1(self):        return "Inside method1"    def __getattribute__(self, name):        attr = super(MyClass, self).__getattribute__(name)        if type(attr) == types.MethodType:            attr = wrapper(attr)        return attr