Подтвердить что ты не робот

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

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

Ответ 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

Ответ 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, я бы использовал его вместо этого.

Ответ 3

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

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

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

Ответ 4

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

Мое обходное решение для этого было довольно простым; установка атрибута метода и последующее тестирование его присутствия. Здесь упрощение всего:

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

UPDATE: На самом деле вызывать method() из run_method() (не так ли?) и передать все аргументы, не модифицированные для метода.

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

Ответ 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