Get class that defined method

66,454

Solution 1

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

Solution 2

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__.

Solution 3

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.

Solution 4

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

Solution 5

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

Share:
66,454

Related videos on Youtube

Jesse Aldridge
Author by

Jesse Aldridge

Updated on July 08, 2022

Comments

  • Jesse Aldridge
    Jesse Aldridge almost 2 years

    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)
    
    • Kathy Van Stone
      Kathy Van Stone about 15 years
      What version of Python are you using? Before 2.2 you could use im_class, but that was changed to show the type of the bound self object.
    • Jesse Aldridge
      Jesse Aldridge about 15 years
      Good to know. But I'm using 2.6.
  • Silas Ray
    Silas Ray over 11 years
    Cls().meth.__self__ just gives you the instance of Cls that is bound to that specific instance of meth. It's analogous to Cls().meth.im_class. If you have class SCls(Cls), SCls().meth.__self__ will get you a SCls instance, not a Cls instance. What the OP wants is to get Cls, which it appears is only available by walking the MRO as @Alex Martelli does.
  • estani
    estani over 11 years
    @sr2222 You are right. I've modified the answer as I have already started though I think Alex solution is more compact.
  • Silas Ray
    Silas Ray over 11 years
    It's a good solution if you need to avoid imports, but since you are basically just re-implementing the MRO, it's not guaranteed to work forever. The MRO will probably stay the same, but it was already changed once in Python's past, and if it is changed again, this code will result is subtle, pervasive bugs.
  • Codie CodeMonkey
    Codie CodeMonkey over 11 years
    Beware, not all classes implement __dict__! Sometimes __slots__ is used. It's probably better to use getattrto test if the method is in the class.
  • sdupton
    sdupton over 11 years
    @DeepYellow: _slots_ are likely not relevant to a method defined on a class, as they describe storage in memory on instances of that class. The methods directly implemented on a class should always be in its _dict_.
  • Yoel
    Yoel almost 10 years
    For Python 3, please refer to this answer.
  • Zitrax
    Zitrax about 9 years
    I am getting: 'function' object has no attribute 'im_class'
  • Alex Martelli
    Alex Martelli almost 9 years
    @Zitrax, it works in Python 2 -- for Python 3, follow the link in Yoel's comment just before yours.
  • RedX
    RedX about 8 years
    In Python 2.7 it does not work. Same error about missing 'im_class'.
  • Dennis Golomazov
    Dennis Golomazov over 6 years
    If meth is a classmethod, replace im_class with im_self. Works in Python 2.7.14. @AlexMartelli you might want to add it to the answer.
  • Marc
    Marc over 6 years
    for those of use who are python 3 only -- is there anything wrong with using meth.__qualname__ ?
  • F1Rumors
    F1Rumors almost 6 years
    Hmm. I am not seeing the defining class when I do that in python 2.7 -- I am getting the class that the method was called on, not the one where it is defined...
  • ulidtko
    ulidtko about 4 years
    Time and time again, "very unlikely scenarios" do happen in programming. Seldomly, causing disasters. In general, the thinking pattern "XY.Z% it won't ever happen" is extremely lousy thinking tool when doing coding. Don't use it. Write 100% correct code.
  • estani
    estani about 4 years
    @ulidtko I think you misread the explanation. It's not about correctness but speed. There is no "perfect" solution that fits all cases, otherwise e.g. there will be only one sorting algorithm. The solution proposed here should be faster in the "rare" case. Since speed comes in 99% percentage of all cases after readability, this solution might be a better solution in "only" that rare case. The code, in case you didn't read it, is 100% correct, if that was what you've feared.
  • Anakhand
    Anakhand over 3 years
    Warning, im_class is a Python 2 only attribute so this won't work with unbound methods in Python 3 (which are just regular functions).
  • FMc
    FMc about 3 years
    This can also be simplified to bar.foo_method.__qualname__ to get 'FooClass.foo_method. I don't know whether there are edge cases for that approach, but it does work for the question at hand.
  • ncaadam
    ncaadam almost 3 years
    all the overly complex answers out there and this worked amazingly in python 3 - good work