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