Get class that defined method

Question:

How can I get the class that defined a method in Python?

I’d want the following example to print “__main__.FooClass“:

class FooClass:
    def foo_method(self):
        print "foo"

class BarClass(FooClass):
    pass

bar = BarClass()
print get_class_that_defined_method(bar.foo_method)
Asked By: Jesse Aldridge

||

Answers:

import inspect

def get_class_that_defined_method(meth):
    for cls in inspect.getmro(meth.im_class):
        if meth.__name__ in cls.__dict__: 
            return cls
    return None
Answered By: Alex Martelli

Thanks Sr2222 for pointing out I was missing the point…

Here’s the corrected approach which is just like Alex’s but does not require to import anything. I don’t think it’s an improvement though, unless there’s a huge hierarchy of inherited classes as this approach stops as soon as the defining class is found, instead of returning the whole inheritance as getmro does. As said, this is a very unlikely scenario.

def get_class_that_defined_method(method):
    method_name = method.__name__
    if method.__self__:    
        classes = [method.__self__.__class__]
    else:
        #unbound method
        classes = [method.im_class]
    while classes:
        c = classes.pop()
        if method_name in c.__dict__:
            return c
        else:
            classes = list(c.__bases__) + classes
    return None

And the Example:

>>> class A(object):
...     def test(self): pass
>>> class B(A): pass
>>> class C(B): pass
>>> class D(A):
...     def test(self): print 1
>>> class E(D,C): pass

>>> get_class_that_defined_method(A().test)
<class '__main__.A'>
>>> get_class_that_defined_method(A.test)
<class '__main__.A'>
>>> get_class_that_defined_method(B.test)
<class '__main__.A'>
>>> get_class_that_defined_method(C.test)
<class '__main__.A'>
>>> get_class_that_defined_method(D.test)
<class '__main__.D'>
>>> get_class_that_defined_method(E().test)
<class '__main__.D'>
>>> get_class_that_defined_method(E.test)
<class '__main__.D'>
>>> E().test()
1

Alex solution returns the same results. As long as Alex approach can be used, I would use it instead of this one.

Answered By: estani

I started doing something somewhat similar, basically the idea was checking whenever a method in a base class had been implemented or not in a sub class. Turned out the way I originally did it I could not detect when an intermediate class was actually implementing the method.

My workaround for it was quite simple actually; setting a method attribute and testing its presence later. Here’s an simplification of the whole thing:

class A():
    def method(self):
        pass
    method._orig = None # This attribute will be gone once the method is implemented

    def run_method(self, *args, **kwargs):
        if hasattr(self.method, '_orig'):
            raise Exception('method not implemented')
        self.method(*args, **kwargs)

class B(A):
    pass

class C(B):
    def method(self):
        pass

class D(C):
    pass

B().run_method() # ==> Raises Exception: method not implemented
C().run_method() # OK
D().run_method() # OK

UPDATE: Actually call method() from run_method() (isn’t that the spirit?) and have it pass all arguments unmodified to the method.

P.S.: This answer does not directly answer the question. IMHO there are two reasons one would want to know which class defined a method; first is to point fingers at a class in debug code (such as in exception handling), and the second is to determine if the method has been re-implemented (where method is a stub meant to be implemented by the programmer). This answer solves that second case in a different way.

I don’t know why no one has ever brought this up or why the top answer has 50 upvotes when it is slow as hell, but you can also do the following:

def get_class_that_defined_method(meth):
    return meth.im_class.__name__

For python 3 I believe this changed and you’ll need to look into .__qualname__.

Answered By: Nick Chapman

In Python 3, if you need the actual class object you can do:

import sys
f = Foo.my_function
vars(sys.modules[f.__module__])[f.__qualname__.split('.')[0]]  # Gets Foo object

If the function could belong to a nested class you would need to iterate as follows:

f = Foo.Bar.my_function
vals = vars(sys.modules[f.__module__])
for attr in f.__qualname__.split('.')[:-1]:
    vals = vals[attr]
# vals is now the class Foo.Bar

Python 3

Solved it in a very simple way:

str(bar.foo_method).split(" ", 3)[-2]

This gives

'FooClass.foo_method'

Split on the dot to get the class and the function name separately

Answered By: firelynx

I found __qualname__ is useful in Python3.

I test it like that:

class Cls(object):
     def func(self):
             print('1')

c = Cls()
print(c.func.__qualname__)
# output is: 'Cls.func'
def single_func():
     print(2)

print(single_func.__module__)
# output: '__main__'
print(single_func.__qualname__)
# output: 'single_func'

After my test, I found another answer here.

if you get this error:

'function' object has no attribute 'im_class'

try this:

import inspect

def get_class_that_defined_method(meth):
    class_func_defided = meth.__globals__[meth.__qualname__.split('.')[0]]
    #full_func_name = "%s.%s.%s"%(class_func_defided.__module__,class_func_defided.__name__,meth.__name__)
    
    if inspect.isfunction(class_func_defided):
        print("%s is not part of a class."%meth.__name__)
        return None
    return class_func_defided

sample test:

class ExampleClass:
    @staticmethod
    def ex_static_method():
        print("hello from static method")
    
    def ex_instance_method(self):
        print("hello from instance method")

def ex_funct(self):
    print("hello from simple function")
    
if __name__ == "__main__":
    static_method_class = get_class_that_defined_method(ExampleClass.ex_static_method)
    static_method_class.ex_static_method()
    
    instance_method_class = get_class_that_defined_method(ExampleClass.ex_instance_method)
    instance_method_class().ex_instance_method()
    
    function_class = get_class_that_defined_method(ex_funct)
Answered By: Adán Escobar