Search code examples
pythonintrospectionmethod-resolution-order

How to check in Python from which class methods is derived?


I have two classes:

class A(object):
  def a(self):
    pass

class B(A):
  def b(self):
    pass

print dir(A)
print dir(B)

How can I check from which class methods is derived in Python?

For example:

getMethodClass(A.a) == A
getMethodClass(B.a) == A
getMethodClass(B.b) == B

Solution

  • Interesting question. Here is how I'd go about it.

    (This works in python2. I haven't tested it in python3, but I won't be surprised if it does not work...)

    You can iterate over all the "nominees" using reversed(inspect.getmro(cls)), and returning the first (by fetching the next value of the iterator) which satisfy the condition that it has the relevant attr, and that attr is the same as the method of the relevant cls.

    Method identity-comparison is done by comparing the im_func attribute of the unbound method.

    import inspect
    
    def getMethodClass(cls, attr):
       return next(
          basecls for basecls in reversed(inspect.getmro(cls))
          if hasattr(basecls, attr)
          and getattr(basecls, attr).im_func is getattr(cls, attr).im_func
       )
    
    getMethodClass(A, 'a')
    => __main__.A
    getMethodClass(B, 'a')
    => __main__.A
    getMethodClass(B, 'b')
    => __main__.B
    
    # an alternative implementation, suggested by @chameleon
    def getAttributeClass(cls, attrName):
      # check first if has attribute
      attr = getattr(cls, attrName)
    
      mro = inspect.getmro(cls)
      # only one class on list
      if len(mro) == 1:
        return cls
    
      # many class on list
      for base in reversed(mro[1:]):
        # check if defined in this base
        try:
          baseAttr = getattr(base, attrName)
        except AttributeError:
          continue
        else:
          if baseAttr.im_func is attr.im_func:
            return base
      # define in top class
      return cls
    

    The function can also have the signature you suggest:

    def getMethodClass(unbound_method):
        cls = unbound_method.im_class
        attr = unbound_method.__name__
        # rest of implementation is the same as before...
    
    getMethodClass(B.a)
    => __main__.A