CodeLab
3Mar/11Off

Python introspection: find which class defines each method

At $work we are currently developing a medium size framework where a common practice is having several levels of subclasses. Scripts written to exercise this framework can either use the subclasses for very specific functionality or restrict themselves to the top-level-class' methods only for maximum portability. But with several levels of inheritance, how do know in which class a method was defined? The solution that follows was a courtesy of Alex Martelli on the online forum stackoverflow:

def getClass(meth):
  '''
  Return the class where the instance method
  was defined
  '''
  for cls in inspect.getmro(meth.im_class):
    if meth.__name__ in cls.__dict__: return cls
  return None

Let's see it in action. Create a module called TestLib.py containing the class that will be our top class: "A":

class A(object):
    def good(self):
        return 'delicious'

    def bad(self):
        return 'like shit'

    def ok(self):
        return 'all right'

Then a script that defines a subclass "B" and runs the introspection code:

#!/usr/bin/env python
import inspect
from TestLib import A

class B(A):
    def __init__(self, veg):
        A.__init__(self)
        self.veg = veg

    def tastes(self):
        print '%s tastes %s' % (self.veg, self.bad())

    def smells(self):
        print '%s smells %s' % (self.veg, self.ok())


def getClass(meth):
  '''
  Return the class for an instance method
  '''
  for cls in inspect.getmro(meth.im_class):
    if meth.__name__ in cls.__dict__: return cls
  return None

if __name__ == '__main__':
    t = B('lettuce')
    t.tastes()

    for m in inspect.getmembers(t, inspect.ismethod):
        cls =  getClass(m[1])
        print '%s \tis defined in class "%s" (%s)' % (
                                 m[0], cls.__name__, 
                                 inspect.getsourcefile(m[1]) )

Line 28 iterates over each method in object t by calling inspect.getmembers. Then Alex's code give us the class on line 29, and we complete the story working out the source file on line 32.

Run it and you will the the following output:

[braudel@lifebook introspect]$ ./test.py 
lettuce tastes like shit
__init__ 	is defined in class "B" (./test.py)
bad 	is defined in class "A" (./TestLib.py)
good 	is defined in class "A" (./TestLib.py)
ok      is defined in class "A" (./TestLib.py)
smells 	is defined in class "B" (./test.py)
tastes 	is defined in class "B" (./test.py)

Lettuce anyone?

Filed under: Python Comments Off
Comments (0) Trackbacks (0)

Sorry, the comment form is closed at this time.

Trackbacks are disabled.