Вопрос

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

Решение

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 возвращает те же результаты.До тех пор, пока можно использовать подход Alex, я бы использовал его вместо этого.

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

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

В 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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top