获取定义方法的类


89

如何获得在Python中定义方法的类?

我想要以下示例打印“ __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)

您正在使用哪个版本的Python?在2.2之前,您可以使用im_class,但已对其进行了更改,以显示绑定的self对象的类型。
凯西·凡·斯通·史东

1
很高兴知道。但是我正在使用2.6。
杰西·奥尔德里奇

Answers:


71
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

1
谢谢,我要
David

当心,并非所有类都实现__dict__!有时__slots__被使用。最好getattr测试一下该方法是否在类中。
Codie CodeMonkey 2013年

16
对于Python 3,请参考此答案
Yoel 2014年

27
我得到:'function' object has no attribute 'im_class'
Zitrax 2015年

5
在Python 2.7中不起作用。关于缺少“ im_class”的相同错误。
RedX

8

感谢Sr2222指出我错过了重点...

这是经过纠正的方法,就像Alex一样,但是不需要导入任何内容。但是,我认为这不是一种改进,除非存在巨大的继承类层次结构,因为一旦找到定义类,该方法就会停止,而不是像getmro这样返回整个继承。如前所述,这是不可能的情况。

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

和示例:

>>> 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解决方案返回相同的结果。只要可以使用Alex方法,我就会用它代替这一方法。


Cls().meth.__self__只是为您提供的实例Cls绑定到的特定实例meth。类似于Cls().meth.im_class。如果有class SCls(Cls)SCls().meth.__self__将为您提供一个SCls实例,而不是Cls实例。OP想要得到的是Cls,它看起来像@Alex Martelli一样只能通过MRO行走。
Silas Ray

@ sr2222你是对的。尽管我认为Alex解决方案更加紧凑,但是我已经开始修改了答案。
estani 2012年

1
如果需要避免导入,这是一个很好的解决方案,但是由于您基本上只是在重新实现MRO,因此不能保证它永远有效。MRO可能保持不变,但是它在Python的过去已经被更改过一次,如果再次更改,则此代码将导致细微的,普遍的错误。
西拉斯·雷

编程一次又一次地发生“非常不可能的情况”。很少会造成灾难。通常,在进行编码时,“永远不会发生的XY.Z%”思维模式是非常糟糕的思维工具。不要使用它。编写100%正确的代码。
ulidtko

@ulidtko我认为您误解了说明。这不是正确性,而是速度。没有适合所有情况的“完美”解决方案,否则,例如,将只有一种排序算法。在“罕见”情况下,此处提出的解决方案应该更快。由于速度在可读性之后占所有情况的99%,因此在“仅”这种罕见情况下,此解决方案可能是更好的解决方案。如果您不读的话,该代码是100%正确的。
estani

6

我不知道为什么没人能提起这个问题,或者为什么最慢的答案在地狱般缓慢的时候会有50次否决,但是您也可以执行以下操作:

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

对于python 3,我相信情况已经改变,您需要研究.__qualname__


2
嗯 我在python 2.7中没有看到定义的类-我得到的是调用该方法的类,而不是定义该方法的类...
F1Rumors 18/08/27

5

在Python 3中,如果您需要实际的类对象,则可以执行以下操作:

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

如果函数可以属于嵌套类,则需要进行以下迭代:

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

1

我开始做一些类似的事情,基本上,我的想法是检查是否在基类中实现了方法,或者是否在子类中实现了方法。原来我的方式是我无法检测到中间类何时真正实现了该方法。

实际上,我的解决方法非常简单;设置方法属性并稍后测试其存在。这是整个过程的简化:

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

更新:实际method()run_method()(不是精神吗?)进行并将其所有未修改的参数传递给该方法。

PS:此答案不能直接回答问题。恕我直言,有两个原因,一个是想知道哪个类定义了一个方法。首先是将手指指向调试代码中的类(例如,在异常处理中),其次是确定该方法是否已重新实现(其中method是应由程序员实现的存根)。这个答案以另一种方式解决了第二种情况。


1

Python 3

用非常简单的方法解决了它:

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

这给

'FooClass.foo_method'

在点上分割以分别获取类和函数名称


哇,真节省!
user3712978
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.