Obter classe que método definido
-
12-09-2019 - |
Pergunta
Como posso obter a classe que define um método em Python?
Eu quero o seguinte exemplo para imprimir "__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)
Solução
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
Outras dicas
Graças Sr2222 por apontar que estava faltando o ponto ...
Aqui está a abordagem corrigido que é apenas como Alex, mas não exige a importação nada. Eu não acho que é uma melhoria embora, a menos que haja um enorme hierarquia de classes herdadas como esta abordagem pára assim que a classe definindo for encontrado, em vez de devolver toda a herança como getmro
faz. Como disse, este é um muito cenário improvável.
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
E o Exemplo:
>>> 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
solução Alex retorna os mesmos resultados. Enquanto abordagem Alex pode ser usado, eu iria utilizá-lo em vez de um presente.
Eu não sei por que ninguém nunca trouxe isso ou por que a resposta de cima tem 50 upvotes quando é lento como o inferno, mas você também pode fazer o seguinte:
def get_class_that_defined_method(meth):
return meth.im_class.__name__
Para python 3 Acredito que isso mudou e você precisa olhar para .__qualname__
.
Eu comecei a fazer algo um pouco semelhante, basicamente, a idéia estava verificando sempre que um método em uma classe base tinha sido implementada ou não em uma classe sub. Saiu do jeito que eu originalmente fez isso eu não conseguia detectar quando uma classe intermediária foi realmente implementar o método.
Minha solução para isso foi realmente muito simples; definindo um método atributo e testar a sua presença depois. Aqui está uma simplificação da coisa toda:
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()
Na verdade chamada de run_method()
(? Não é que o espírito) e tê-lo passar todos os argumentos não modificados para o método
P.S .: Essa resposta não respondeu diretamente à pergunta. IMHO há duas razões que gostaria de saber qual classe definido um método; primeiro é para dedos de ponto a uma classe em código de depuração (tal como no tratamento de exceção), e a segunda é para determinar se o método tem sido re-implantado (onde método é um topo destina-se a ser implementado pelo programador). Esta resposta resolve o segundo caso de uma maneira diferente.
Em Python 3, se você precisa do objeto de classe real que você pode fazer:
import sys
f = Foo.my_function
vars(sys.modules[f.__module__])[f.__qualname__.split('.')[0]] # Gets Foo object
Se a função poderia pertencer a uma classe aninhada que você precisa para fazer uma iteração da seguinte forma:
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