Get Class That Defined Method

Get class that defined method

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

Find class in which a method is defined

If you need this in Python 3.x, please see my other answer—the closure cell __class__ is all you need.


If you need to do this in CPython 2.6-2.7, RickyA's answer is close, but it doesn't work, because it relies on the fact that this method is not overriding any other method of the same name. Try adding a Foo.do_it method in his answer, and it will print out Foo, not SomeClass

The way to solve that is to find the method whose code object is identical to the current frame's code object:

def do_it(self):
mro = inspect.getmro(self.__class__)
method_code = inspect.currentframe().f_code
method_name = method_code.co_name
for base in reversed(mro):
try:
if getattr(base, method_name).func_code is method_code:
print(base.__name__)
break
except AttributeError:
pass

(Note that the AttributeError could be raised either by base not having something named do_it, or by base having something named do_it that isn't a function, and therefore doesn't have a func_code. But we don't care which; either way, base is not the match we're looking for.)

This may work in other Python 2.6+ implementations. Python does not require frame objects to exist, and if they don't, inspect.currentframe() will return None. And I'm pretty sure it doesn't require code objects to exist either, which means func_code could be None.

Meanwhile, if you want to use this in both 2.7+ and 3.0+, change that func_code to __code__, but that will break compatibility with earlier 2.x.


If you need CPython 2.5 or earlier, you can just replace the inpsect calls with the implementation-specific CPython attributes:

def do_it(self):
mro = self.__class__.mro()
method_code = sys._getframe().f_code
method_name = method_code.co_name
for base in reversed(mro):
try:
if getattr(base, method_name).func_code is method_code:
print(base.__name__)
break
except AttributeError:
pass

Note that this use of mro() will not work on classic classes; if you really want to handle those (which you really shouldn't want to…), you'll have to write your own mro function that just walks the hierarchy old-school… or just copy it from the 2.6 inspect source.

This will only work in Python 2.x implementations that bend over backward to be CPython-compatible… but that includes at least PyPy. inspect should be more portable, but then if an implementation is going to define frame and code objects with the same attributes as CPython's so it can support all of inspect, there's not much good reason not to make them attributes and provide sys._getframe in the first place…

How do I get list of methods in a Python class?

An example (listing the methods of the optparse.OptionParser class):

>>> from optparse import OptionParser
>>> import inspect
#python2
>>> inspect.getmembers(OptionParser, predicate=inspect.ismethod)
[([('__init__', <unbound method OptionParser.__init__>),
...
('add_option', <unbound method OptionParser.add_option>),
('add_option_group', <unbound method OptionParser.add_option_group>),
('add_options', <unbound method OptionParser.add_options>),
('check_values', <unbound method OptionParser.check_values>),
('destroy', <unbound method OptionParser.destroy>),
('disable_interspersed_args',
<unbound method OptionParser.disable_interspersed_args>),
('enable_interspersed_args',
<unbound method OptionParser.enable_interspersed_args>),
('error', <unbound method OptionParser.error>),
('exit', <unbound method OptionParser.exit>),
('expand_prog_name', <unbound method OptionParser.expand_prog_name>),
...
]
# python3
>>> inspect.getmembers(OptionParser, predicate=inspect.isfunction)
...

Notice that getmembers returns a list of 2-tuples. The first item is the name of the member, the second item is the value.

You can also pass an instance to getmembers:

>>> parser = OptionParser()
>>> inspect.getmembers(parser, predicate=inspect.ismethod)
...

get a class name of calling method

You can get the calling frame object with inspect.currentframe() and get the object that self is bound to through its f_locals attribute:

import inspect

def get_some_info():
# get the call frame of the calling method
frame = inspect.currentframe().f_back
try:
# try to access the caller's "self"
try:
self_obj = frame.f_locals['self']
except KeyError:
return None

# get the class of the "self" and return its name
return type(self_obj).__name__
finally:
# make sure to clean up the frame at the end to avoid ref cycles
del frame

The disadvantage of this is that it relies on the first parameter to be named "self". There are a few cases where we use different names, for example when writing a metaclass:

class MyMeta(type):
def __call__(cls, *args, **kwargs):
get_some_info() # won't work!

And if you have a function with a self variable, it can produce unexpected results:

def not_a_method():
self = 3
print(get_some_info()) # output: int

We can solve both of these problems, but it takes a lot of work. We can inspect the name of the "self" parameter through the calling code object's co_varnames attribute. And in order to check whether the calling function is really a method defined in a class, we can loop through the self's MRO and try to find the method that called us. The end result is this monstrosity:

def get_some_info():
# get the call frame of the calling method
frame = inspect.currentframe().f_back
try:
# find the name of the first variable in the calling
# function - which is hopefully the "self"
codeobj = frame.f_code
try:
self_name = codeobj.co_varnames[0]
except IndexError:
return None

# try to access the caller's "self"
try:
self_obj = frame.f_locals[self_name]
except KeyError:
return None

# check if the calling function is really a method
self_type = type(self_obj)
func_name = codeobj.co_name

# iterate through all classes in the MRO
for cls in self_type.__mro__:
# see if this class has a method with the name
# we're looking for
try:
method = vars(cls)[func_name]
except KeyError:
continue

# unwrap the method just in case there are any decorators
try:
method = inspect.unwrap(method)
except ValueError:
pass

# see if this is the method that called us
if getattr(method, '__code__', None) is codeobj:
return self_type.__name__

# if we didn't find a matching method, return None
return None
finally:
# make sure to clean up the frame at the end to avoid ref cycles
del frame

This should handle pretty much everything you throw at it correctly:

class Base:
def my_method(whatever):
print(get_some_info())

@functools.lru_cache() # could be any properly implemented decorator
def my_decorated_method(foo):
print(get_some_info())

@classmethod
def my_class_method(cls):
print(get_some_info())

class A(Base):
pass

def not_a_method(self=3):
print(get_some_info())

A().my_method() # prints "A"
A().my_decorated_method() # prints "A"
A.my_class_method() # prints "None"
not_a_method() # prints "None"
print(get_some_info()) # prints "None"

In Python, how can you get the name of a member function's class?

testFunc.im_class

https://docs.python.org/reference/datamodel.html#the-standard-type-hierarchy

im_class is the class of im_self for
bound methods or the class that asked
for the method for unbound methods

how to get class name of a class static method through inspection

That is the very definition of a static method: it is called without a class argument (as in class methods) and without an instance argument (as in instance methods). The only real difference between a function declared in module scope and a static method is that the method name is defined in the class' namespace and not in the module's namespace.

In other words, you can't get to the class object directly. You can get the function name by examining the stack (although I am not sure how useful it is):

>>> import sys
>>> import traceback
>>> class A(object):
@staticmethod
def a():
trace()
>>> def trace():
print traceback.extract_stack(sys._getframe())[-3][3]
>>> A.a()
A.a()

And given the name, you could get to the class object by extracting from the name and looking it up in the module's namespace...

For reference:

frame @ -1 : call to traceback.extract_stack()

frame @ -2 : call to trace()

frame @ -3 : call to A.a()

checking if a method is defined on the class

Use this:

C.instance_methods(false).include?(:a)
C.instance_methods(false).include?(:b)
C.instance_methods(false).include?(:c)

The method instance_methods return an Array of methods that an instance of this class would have. Passing false as first parameter returns only methods of this class, not methods of super classes.

So C.instance_methods(false) returns the list of methods defined by C.

Then you just have to check if that method is in the returned Array (this is what the include? calls do).

See docs



Related Topics



Leave a reply



Submit