【问题标题】:Get class that defined method获取定义方法的类
【发布时间】:2010-10-31 22:55:53
【问题描述】:

如何获取在 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 对象的类型。
  • 很高兴知道。但我使用的是 2.6。

标签: python python-2.6 python-datamodel


【解决方案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

【讨论】:

  • 当心,不是所有的类都实现__dict__!有时使用__slots__。最好使用getattr来测试方法是否在类中。
  • Python 3请参考this answer
  • 我收到:'function' object has no attribute 'im_class'
  • 在 Python 2.7 中它不起作用。关于缺少“im_class”的相同错误。
  • 对于那些只使用python 3的人——使用meth.__qualname__有什么问题吗?
【解决方案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 解决方案返回相同的结果。只要能用Alex的方法,我就用它代替这个。

【讨论】:

  • Cls().meth.__self__ 只是为您提供绑定到 meth 的特定实例的 Cls 实例。它类似于Cls().meth.im_class。如果你有class SCls(Cls)SCls().meth.__self__ 会给你一个SCls 实例,而不是Cls 实例。 OP 想要的是获得Cls,它似乎只能通过像@Alex Martelli 那样步行 MRO 来获得。
  • @sr2222 你是对的。尽管我认为 Alex 解决方案更紧凑,但我已经修改了答案。
  • 如果您需要避免导入,这是一个很好的解决方案,但由于您基本上只是重新实现 MRO,因此不能保证它永远有效。 MRO 可能会保持不变,但它在 Python 过去已经更改过一次,如果再次更改,此代码将导致细微的、普遍存在的错误。
  • 编程中确实会一次又一次地发生“非常不可能的情况”。很少,造成灾难。一般来说,“XY.Z% it won't ever occur”的思维模式是编码时非常糟糕的思维工具。不要使用它。编写 100% 正确的代码。
  • @ulidtko 我认为您误读了解释。这不是关于正确性,而是关于速度。没有适合所有情况的“完美”解决方案,否则例如只有一种排序算法。在“罕见”情况下,此处提出的解决方案应该更快。由于在可读性之后,所有情况下 99% 的情况都具有速度,因此该解决方案可能是“仅”在极少数情况下的更好解决方案。代码,如果你没有读过,100% 正确,如果那是你所担心的。
【解决方案3】:

我不知道为什么没有人提出这个问题,或者为什么当它慢得要命的时候,最佳答案有 50 个赞成票,但您也可以执行以下操作:

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

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

【讨论】:

  • 嗯。当我在 python 2.7 中这样做时,我没有看到定义类——我得到的是调用该方法的类,而不是定义它的类...
  • 所有过于复杂的答案,这在 python 3 中效果惊人——干得好
【解决方案4】:

在 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

【讨论】:

    【解决方案5】:

    我开始做一些类似的事情,基本上这个想法是检查基类中的方法是否已在子类中实现。结果是我最初做的方式我无法检测到中间类何时实际实现该方法。

    我的解决方法实际上很简单;设置方法属性并稍后测试它的存在。这是整个事情的简化:

    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
    

    更新:实际上从run_method() 调用method()(这不是精神吗?)并让它将所有未修改的参数传递给方法。

    P.S.:这个答案没有直接回答这个问题。恕我直言,有两个原因想知道哪个类定义了一个方法;第一个是在调试代码中指出一个类(例如在异常处理中),第二个是确定该方法是否已被重新实现(其中方法是一个打算由程序员实现的存根)。这个答案以不同的方式解决了第二种情况。

    【讨论】:

      【解决方案6】:

      Python 3

      用很简单的方法解决了:

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

      这给了

      'FooClass.foo_method'

      在点上拆分,分别得到类和函数名

      【讨论】:

      • 这也可以简化为bar.foo_method.__qualname__得到'FooClass.foo_method。我不知道这种方法是否存在极端情况,但它确实适用于手头的问题。
      猜你喜欢
      • 2013-10-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-07
      • 2012-05-23
      • 2016-09-17
      相关资源
      最近更新 更多