Search code examples
pythondecorator

Can a decorator of an instance method access the class?


I have something roughly like the following. Basically I need to access the class of an instance method from a decorator used upon the instance method in its definition.

def decorator(view):
    # do something that requires view's class
    print view.im_class
    return view

class ModelA(object):
    @decorator
    def a_method(self):
        # do some stuff
        pass

The code as-is gives:

AttributeError: 'function' object has no attribute 'im_class'

I found similar question/answers - Python decorator makes function forget that it belongs to a class and Get class in Python decorator - but these rely upon a workaround that grabs the instance at run-time by snatching the first parameter. In my case, I will be calling the method based upon the information gleaned from its class, so I can't wait for a call to come in.


Solution

  • If you are using Python 2.6 or later you could use a class decorator, perhaps something like this (warning: untested code).

    def class_decorator(cls):
       for name, method in cls.__dict__.iteritems():
            if hasattr(method, "use_class"):
                # do something with the method and class
                print name, cls
       return cls
    
    def method_decorator(view):
        # mark the method as something that requires view's class
        view.use_class = True
        return view
    
    @class_decorator
    class ModelA(object):
        @method_decorator
        def a_method(self):
            # do some stuff
            pass
    

    The method decorator marks the method as one that is of interest by adding a "use_class" attribute - functions and methods are also objects, so you can attach additional metadata to them.

    After the class has been created the class decorator then goes through all the methods and does whatever is needed on the methods that have been marked.

    If you want all the methods to be affected then you could leave out the method decorator and just use the class decorator.