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)
Foi útil?

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
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top