Obtener clase que define el método


¿Cómo puedo obtener la clase que definió un método en Python?

Me gustaría que se imprimiera el siguiente ejemplo"__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)
Author: jamylak, 2009-06-07

4 answers

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
 57
Author: Alex Martelli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-20 19:44:30

Gracias Sr2222 por señalar que estaba perdiendo el punto...

Aquí está el enfoque corregido que es igual que el de Alex, pero no requiere importar nada. Sin embargo, no creo que sea una mejora, a menos que haya una gran jerarquía de clases heredadas, ya que este enfoque se detiene tan pronto como se encuentra la clase definitoria, en lugar de devolver toda la herencia como lo hace getmro. Como se ha dicho, este es un muy escenario improbable.

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

Y el Ejemplo:

>>> 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 solution devuelve los mismos resultados. Mientras se pueda usar el enfoque de Alex, lo usaría en lugar de este.

 5
Author: estani,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-30 13:04:15

No se por qué nadie ha mencionado esto o por qué la respuesta superior tiene 50 votos positivos cuando es lenta como el infierno, pero también puedes hacer lo siguiente:

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

Para python 3 creo que esto cambió y tendrá que mirar en .__qualname__.

 2
Author: Nick Chapman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-02 21:09:46

Empecé a hacer algo similar, básicamente la idea era comprobar cada vez que un método en una clase base se había implementado o no en una subclase. Resultó que la forma en que lo hice originalmente no pude detectar cuando una clase intermedia estaba implementando el método.

Mi solución para ello era bastante simple en realidad; establecer un atributo method y probar su presencia más tarde. He aquí una simplificación de todo:

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

ACTUALIZACIÓN: En realidad llamar method() de run_method() (¿no es ese el espíritu?) y que pase todos los argumentos sin modificar al método.

P.d.: Esta respuesta no responde directamente a la pregunta. En mi humilde opinión, hay dos razones por las que uno querría saber qué clase definió un método; primero es señalar con el dedo a una clase en el código de depuración (como en el manejo de excepciones), y el segundo es determinar si el método ha sido re-implementado (donde el método es un stub destinado a ser implementado por el programador). Esta respuesta resuelve ese segundo caso de una manera diferente.

 1
Author: Thomas Guyot-Sionnest,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-07 07:10:34