获取定义方法的类

Get class that defined method

提问人:Jesse Aldridge 提问时间:6/7/2009 最后编辑:jamylakJesse Aldridge 更新时间:7/23/2023 访问量:84379

问:

如何获取在 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.6 python-数据模型

评论

0赞 Kathy Van Stone 6/7/2009
您使用的是哪个版本的 Python?在 2.2 之前,您可以使用 im_class,但已更改为显示绑定的 self 对象的类型。
1赞 Jesse Aldridge 6/7/2009
很高兴知道。但我使用的是 2.6。

答:

80赞 Alex Martelli 6/7/2009 #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

评论

3赞 Codie CodeMonkey 3/12/2013
请注意,并非所有类都实现!有时使用。最好用它来测试该方法是否在类中。__dict____slots__getattr
18赞 Yoel 9/21/2014
对于 ,请参阅此答案Python 3
38赞 Zitrax 6/24/2015
我得到:'function' object has no attribute 'im_class'
5赞 RedX 5/23/2016
在 Python 2.7 中,它不起作用。关于缺少“im_class”的相同错误。
5赞 Marc 10/26/2017
对于那些只使用 Python 3 的人 - 使用有什么问题吗?meth.__qualname__
8赞 estani 11/29/2012 #2

感谢 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 解决方案返回相同的结果。只要可以使用亚历克斯的方法,我就会使用它而不是这个方法。

评论

0赞 Silas Ray 12/20/2012
Cls().meth.__self__只是给你一个实例,它绑定到那个特定的实例。它类似于 .如果有 ,将为您提供一个实例,而不是实例。OP 想要的是得到 ,似乎只有像 Martelli 那样通过步行 MRO @Alex才能获得。ClsmethCls().meth.im_classclass SCls(Cls)SCls().meth.__self__SClsClsCls
0赞 estani 12/30/2012
@sr2222 你是对的。我已经修改了答案,因为我已经开始了,尽管我认为 Alex 解决方案更紧凑。
1赞 Silas Ray 1/2/2013
如果您需要避免导入,这是一个很好的解决方案,但由于您基本上只是重新实现 MRO,因此不能保证它永远有效。MRO 可能会保持不变,但它在 Python 的过去已经更改过一次,如果再次更改,这段代码将导致微妙的、普遍的错误。
0赞 ulidtko 5/7/2020
一次又一次,“非常不可能的场景”确实发生在编程中。很少,造成灾难。一般来说,思维模式“XY.Z% 它永远不会发生“是进行编码时非常糟糕的思考工具。不要使用它。编写 100% 正确的代码。
0赞 estani 5/10/2020
@ulidtko 我认为你误读了解释。这不是关于正确性,而是关于速度。没有适合所有情况的“完美”解决方案,否则,例如,将只有一种排序算法。在“罕见”的情况下,这里提出的解决方案应该更快。由于速度在所有情况下的 99% 都具有可读性,因此在“仅”这种罕见情况下,此解决方案可能是更好的解决方案。如果你没有读过它,代码是 100% 正确的,如果这是你所担心的。
1赞 Thomas Guyot-Sionnest 10/16/2014 #3

我尝试做类似的事情来检查基类中的存根方法是否已在子类中实现。无论我尝试哪种方式,我都无法检测到中间类何时实际实现该方法(下面的情况)。d.run_method()

最后,我通过设置方法属性并稍后测试其存在来做到这一点:

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.:这并不能直接回答这个问题......恕我直言,人们想知道哪个类定义了方法有两个主要原因;一种是指向调试代码中的类(例如在异常处理中),另一种是确定方法是否已重新实现(其中 method 是程序员要实现的存根)。这个答案以不同的方式解决了第二种情况。

评论

0赞 Thomas Guyot-Sionnest 4/16/2023
注意:我可能没有仔细研究,或者这可能是 Python2 的限制,因为我刚刚对这段代码做了一个操作,它清楚地打印了所属类,所以数据在某个地方。pprint(self.method)
12赞 Nick Chapman 10/3/2017 #4

我不知道为什么没有人提出这个问题,或者为什么最高答案在速度慢得要命时有 50 个赞成票,但你也可以做到以下几点:

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

对于 python 3,我相信这发生了变化,您需要研究..__qualname__

评论

3赞 F1Rumors 8/27/2018
嗯。当我在 python 2.7 中执行此操作时,我没有看到定义类 - 我得到的是调用该方法的类,而不是定义它的类......
0赞 ncaadam 7/30/2021
所有过于复杂的答案都在那里,这在 Python 3 中效果惊人 - 干得好
1赞 Princy 7/10/2022
meth.__qualname__为您提供方法的限定名称,其中包括定义方法的类的名称。
8赞 Matthew D. Scholefield 4/20/2019 #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赞 firelynx 9/17/2020 #6

Python 3 (英语)

以一种非常简单的方式解决了它:

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

这给了

'FooClass.foo_method'

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

评论

4赞 FMc 4/10/2021
这也可以简化为得到 .我不知道这种方法是否有边缘情况,但它确实适用于手头的问题。bar.foo_method.__qualname__'FooClass.foo_method
2赞 我要改名叫嘟嘟 11/18/2022 #7

我发现__qualname__在 Python3 中很有用。

我是这样测试的:

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'

测试结束后,我在这里找到了另一个答案。

0赞 Adán Escobar 2/8/2023 #8

如果收到此错误:

'function' object has no attribute 'im_class'

试试这个:

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

样品测试:

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)
0赞 AntumDeluge 4/11/2023 #9

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()

输出:

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

Python 2.7 或 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()
0赞 Ori Yarden PhD 4/16/2023 #10

我们可以使用方法解析顺序或从中查找名称,它有:mro()SomeClasssome_method

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])

输出:

<class '__main__.SomeClass'>

这样我们就可以找到所属类的名称,即使它继承了:some_methodSomeOtherClass

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])

输出:

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

或具有 (或) 的所有类的名称:some_methodsome_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__)])

输出:

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

要获得 ing 中的 s,请执行以下操作:__name__str

print([cls.__name__ ...
0赞 Markus Dutschke 4/21/2023 #11

只需使用属性即可__qualname__

ClassOrInstance.method.__qualname__生成一个字符串Class.method

代码,经测试使用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'

下一步

如果要检查代码中的实现位置,可以执行以下操作

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

这里演示的是 a 而不是 .classinstance

1赞 MarsaPalas 6/16/2023 #12

inspect._findclass似乎适用于任何函数/方法。

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
1赞 Glyph 7/23/2023 #13

从 Python 3.6 开始,您已经能够用作描述符的钩子,如本次对该问题的重复回答中所述。__set_name__