Получить класс, который определил метод


Как я могу получить класс, который определил метод в 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)
4 58

4 ответа:

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

спасибо Sr2222 за указание, что я упустил суть...

вот исправленный подход, который так же, как у Алекса, но не требует ничего импортировать. Я не думаю, что это улучшение, хотя, если нет огромной иерархии унаследованных классов, поскольку этот подход останавливается, как только определяющий класс найден, вместо того, чтобы возвращать все наследование как 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 возвращает те же результаты. Пока можно использовать подход Алекса, я бы использовал его вместо этого.

Я не знаю, почему никто никогда не поднимал этот вопрос или почему верхний ответ имеет 50 upvotes, когда он медленный, как ад, но вы также можете сделать следующее:

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

для python 3 я считаю, что это изменилось, и вам нужно будет заглянуть в .__qualname__.

Я начал делать что-то похожее, в основном идея заключалась в проверке всякий раз, когда метод в базовом классе был реализован или нет в подклассе. Оказалось, что я изначально сделал это, я не мог обнаружить, когда промежуточный класс фактически реализовывал метод.

мой обходной путь для этого был довольно прост на самом деле; установка метода атрибут и проверка его присутствия позже. Вот упрощение всего вещь:

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() (разве это не дух?) и пусть он передаст все аргументы без изменений в метод.

P. S.: Данный ответ не прямо ответить на вопрос. IMHO есть две причины, по которым нужно знать, какой класс определил метод; Во-первых, указать пальцем на класс в отладочном коде (например, в обработке исключений), а во-вторых, определить, был ли метод повторно реализован (где метод-это заглушка для реализации программистом). Этот ответ решает этот второй случай по-другому.