Получить класс, который определил метод
-
12-09-2019 - |
Вопрос
Как я могу получить класс, который определил метод в 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