2009-06-07 14 views
52

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

me gustaría que el siguiente ejemplo 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) 
+0

¿Qué versión de Python estás usando? Antes 2.2 podías usar im_class, pero eso fue cambiado para mostrar el tipo del objeto self encuadernado. –

+1

Es bueno saberlo. Pero estoy usando 2.6. –

Respuesta

54
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 
+0

¡Funciona, gracias! –

+0

¡De nada! –

+1

Gracias, me hubiera tomado un tiempo darme cuenta por mi cuenta – David

5

Gracias Sr2222 por señalar que faltaba el punto ...

Aquí es el enfoque que es corregido al igual que Alex, pero no requiere importar nada. 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 de definición, en lugar de devolver la herencia completa como getmro. Como se dijo, este es un escenario poco probable de muy.

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 

solución Alex devuelve los mismos resultados. Siempre que se pueda usar el enfoque de Alex, lo usaría en lugar de este.

+0

'Cls(). Meth .__ self__' solo le da la instancia de' Cls' que está vinculada a esa instancia específica de 'meth'. Es análogo a 'Cls(). Meth.im_class'. Si tiene 'clase SCls (Cls)', 'SCls(). Meth .__ self__' obtendrá una instancia' SCls', no una instancia 'Cls'. Lo que OP quiere es obtener 'Cls', que parece que solo está disponible al recorrer el MRO como lo hace @Alex Martelli. –

+0

@ sr2222 Tienes razón. He modificado la respuesta porque ya comencé, aunque creo que la solución de Alex es más compacta. – estani

+0

Es una buena solución si necesita evitar las importaciones, pero como básicamente está reiniciando el MRO, no se garantiza que funcione para siempre. El MRO probablemente se mantendrá igual, pero ya se cambió una vez en el pasado de Python, y si se cambia de nuevo, este código dará como resultado errores sutiles y omnipresentes. –

1

Empecé a hacer algo similar, básicamente la idea era verificar cada vez que se implementaba un método en una clase base o no en una subclase. Resultó como lo hice originalmente. No pude detectar cuándo una clase intermedia realmente estaba implementando el método.

Mi solución para esto fue bastante simple en realidad; estableciendo un método atributo y probando su presencia más tarde. He aquí una simplificación de todo el asunto:

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 que el espíritu?) Y tienen que pasar todos los argumentos sin modificar el método.

P.S .: Esta respuesta no responde directamente 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 reimplementado (donde el método es un apéndice destinado a ser implementado por el programador). Esta respuesta resuelve ese segundo caso de una manera diferente.

2

No sé por qué no se ha llevado nunca este arriba o por qué la respuesta superior tiene 50 upvotes cuando es lento como el infierno, pero también se puede hacer lo siguiente:

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

para Python 3 Creo que esto cambió y tendrás que mirar en .__qualname__.

Cuestiones relacionadas