119

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)
jamylak
  • 128,818
  • 30
  • 231
  • 230
Jesse Aldridge
  • 7,991
  • 9
  • 48
  • 75

13 Answers13

80
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
Russia Must Remove Putin
  • 374,368
  • 89
  • 403
  • 331
Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • 3
    Beware, not all classes implement `__dict__`! Sometimes `__slots__` is used. It's probably better to use `getattr`to test if the method is in the class. – Codie CodeMonkey Mar 11 '13 at 22:07
  • @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__. – sdupton Mar 21 '13 at 17:25
  • 18
    For `Python 3`, please refer to [this answer](http://stackoverflow.com/a/25959545/3903832). – Yoel Sep 21 '14 at 13:13
  • 37
    I am getting: `'function' object has no attribute 'im_class'` – Zitrax Jun 24 '15 at 09:00
  • @Zitrax, it works in Python 2 -- for Python 3, follow the link in Yoel's comment just before yours. – Alex Martelli Jul 29 '15 at 22:24
  • 5
    In Python 2.7 it does not work. Same error about missing 'im_class'. – RedX May 23 '16 at 11:54
  • 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. – Dennis Golomazov Oct 19 '17 at 22:25
  • 5
    for those of use who are python 3 only -- is there anything wrong with using `meth.__qualname__` ? – Marc Oct 26 '17 at 14:25
11

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

Nick Chapman
  • 4,402
  • 1
  • 27
  • 41
  • 3
    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... – F1Rumors Aug 27 '18 at 14:00
  • all the overly complex answers out there and this worked amazingly in python 3 - good work – ncaadam Jul 30 '21 at 13:22
  • 1
    `meth.__qualname__` gives you the qualified name of the method which includes the name of the class in which it is defined. – Princy Jul 10 '22 at 04:50
8

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.

estani
  • 24,254
  • 2
  • 93
  • 76
  • `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. – Silas Ray Dec 20 '12 at 15:03
  • @sr2222 You are right. I've modified the answer as I have already started though I think Alex solution is more compact. – estani Dec 30 '12 at 13:01
  • 1
    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. – Silas Ray Jan 02 '13 at 14:20
  • 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. – ulidtko May 07 '20 at 11:01
  • @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. – estani May 10 '20 at 10:45
  • 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). – Anakhand Dec 13 '20 at 11:23
8

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
Matthew D. Scholefield
  • 2,977
  • 3
  • 31
  • 42
2

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.

1

I tried doing something similar to check if a stub method in a base class had been implemented or not in a subclass. Whichever way I tried I could not detect when an intermediate class was actually implementing the method (d.run_method() case below).

I finally did it by setting a method attribute and testing its presence later:

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

P.S.: This doesn't answer directly the question... IMHO there are two major reasons one would want to know which class defined a method; one is to point fingers at a class in debug code (such as in exception handling), and another 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.

Thomas Guyot-Sionnest
  • 2,251
  • 22
  • 17
  • Nb: I probably haven't looked hard enough or maybe that was a Python2 limitation as I just did a `pprint(self.method)` on this code and it clearly prints the owning class, so the data is there somewhere. – Thomas Guyot-Sionnest Apr 15 '23 at 20:17
1

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

firelynx
  • 30,616
  • 9
  • 91
  • 101
  • 4
    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. – FMc Apr 09 '21 at 23:18
1

Since Python 3.6, you have been able to use __set_name__ as a hook on a descriptor, as described in this answer to a duplicate of this question.

Glyph
  • 31,152
  • 11
  • 87
  • 129
0

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)
Adán Escobar
  • 1,729
  • 9
  • 15
0

Another solution for Python 3:

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

class BarClass(FooClass):
  pass

class BazClass(BarClass):
  pass

baz = BazClass()

tmp = baz.foo_method.__self__.__class__
while hasattr(tmp.__base__, "foo_method"):
  tmp = tmp.__base__

print("defining class: {}".format(tmp))
tmp().foo_method()

Output:

defining class: <class '__main__.FooClass'>
foo

Python 2.7 or 3:

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

class BarClass(FooClass):
  pass

class BazClass(BarClass):
  pass

baz = BazClass()

tmp = baz.foo_method.__self__.__class__
while len(tmp.__bases__) > 0 and hasattr(tmp.__bases__[0], "foo_method"):
  tmp = tmp.__bases__[0]

print("defining class: {}".format(tmp))
tmp().foo_method()
AntumDeluge
  • 490
  • 1
  • 5
  • 13
0

We can use method resolution order or mro() to find the name of SomeClass from some_method it has:

class SomeClass:
    def __init__(self):
        self.foo = 100
    
    def some_method(self):
        return self.foo

a = SomeClass()
print(a.some_method.__self__.__class__.mro()[0])

Outputs:

<class '__main__.SomeClass'>

that way we can find the name of the class that some_method belongs to even if it's inherited by SomeOtherClass:

class SomeClass:
    def __init__(self):
        self.foo = 100
    
    def some_method(self):
        return self.foo

class SomeOtherClass(SomeClass):
    def __init__(self):
        super().__init__()
        self.other_foo = 1
    
    def some_other_method(self):
        return self.other_foo

a = SomeOtherClass()
print([cls for cls in a.some_method.__self__.__class__.mro() if cls.__dict__.__contains__(a.some_method.__name__)][0])
print([cls for cls in a.some_other_method.__self__.__class__.mro() if cls.__dict__.__contains__(a.some_other_method.__name__)][0])

Outputs:

<class '__main__.SomeClass'>
<class '__main__.SomeOtherClass'>

or the names of all the classes that have some_method (or some_other_method):

print([cls for cls in a.some_method.__self__.__class__.mro() if hasattr(cls, a.some_method.__name__)])
print([cls for cls in a.some_other_method.__self__.__class__.mro() if hasattr(cls, a.some_other_method.__name__)])

Outputs:

[<class '__main__.SomeOtherClass'>, <class '__main__.SomeClass'>]
[<class '__main__.SomeOtherClass'>]

To get the __name__s in string:

print([cls.__name__ ...
Ori Yarden PhD
  • 1,287
  • 1
  • 4
  • 8
0

simply use the __qualname__ attribute

ClassOrInstance.method.__qualname__ yields a Class.method string

Code, tested with Python 3.8.8

class Grandparent:
    def test(self):
            print("grandparent")

class Parent(Grandparent):
    def test(self):
        print("parent")

class Child(Parent):
    pass

class Uncle(Grandparent):
    pass
>>> Grandparent().test.__qualname__
'Grandparent.test'
>>> Parent().test.__qualname__
'Parent.test'
>>> Child().test.__qualname__
'Parent.test'
>>> Uncle().test.__qualname__
'Grandparent.test'

Next step

If you want to check for the place of implementation in the code, you can do

>>> Uncle.test.__qualname__.split(".")[0] == Grandparent.__name__
True
>>> Child.test.__qualname__.split(".")[0] == Grandparent.__name__
False
>>> Child.test.__qualname__.split(".")[0] == Parent.__name__
True

Here it is demonstrated for a class instead of an instance.

Markus Dutschke
  • 9,341
  • 4
  • 63
  • 58
0

inspect._findclass seems to be working fine for any function/method.

import inspect
import sys


class SomeClass:
    @staticmethod
    def staticMethod():
        print('staticMethod')

    @classmethod
    def classMethod(cls):
        print('classMethod')

    def someMethod(self):
        print('bound method')

def myGlblFunc():
    print('Global function')


if __name__ == '__main__':
    static_method = SomeClass.staticMethod
    class_method = SomeClass.classMethod
    unbound_method = SomeClass.someMethod
    bound_method = SomeClass().someMethod
    glbl_func = myGlblFunc

    static_method()
    print(inspect._findclass(static_method), end='\n\n')

    class_method()
    print(inspect._findclass(class_method), end='\n\n')

    print('unbound method')
    print(inspect._findclass(unbound_method), end='\n\n')

    bound_method()
    print(inspect._findclass(bound_method), end='\n\n')

    glbl_func()
    print(inspect._findclass(glbl_func), end='\n\n')

    sys.exit(0)

# Output:
    # staticMethod
    # <class '__main__.SomeClass'>
    #
    # classMethod
    # <class '__main__.SomeClass'>
    #
    # unbound method
    # <class '__main__.SomeClass'>
    #
    # bound method
    # <class '__main__.SomeClass'>
    #
    # Global function
    # None
MarsaPalas
  • 375
  • 1
  • 6
  • 19