¿Cómo detectar la sobrecarga de métodos en las subclases en python?

Tengo una clase que es una súper clase para muchas otras clases. Me gustaría saber (en el init () de mi superclase si la subclase ha anulado un método específico).

Intenté lograr esto con un método de clase, pero los resultados fueron incorrectos:

class Super: def __init__(self): if self.method == Super.method: print 'same' else: print 'different' @classmethod def method(cls): pass class Sub1(Super): def method(self): print 'hi' class Sub2(Super): pass Super() # should be same Sub1() # should be different Sub2() # should be same >>> same >>> different >>> different 

¿Hay alguna forma para que una superclase sepa si una subclase ha anulado un método?

Puedes usar tu propio decorador. Pero esto es un truco y solo funcionará en las clases en las que usted controla la implementación.

 def override(method): method.is_overridden = True return method class Super: def __init__(self): if hasattr(self.method, 'is_overridden'): print 'different' else: print 'same' @classmethod def method(cls): pass class Sub1(Super): @override def method(self): print 'hi' class Sub2(Super): pass Super() # should be same Sub1() # should be different Sub2() # should be same >>> same >>> different >>> same 

Parece más simple y suficiente para hacer esto comparando el subconjunto común de los diccionarios de una instancia y la clase base en sí, por ejemplo:

 def detect_overridden(cls, obj): common = cls.__dict__.keys() & obj.__class__.__dict__.keys() diff = [m for m in common if cls.__dict__[m] != obj.__class__.__dict__[m]] print(diff) def f1(self): pass class Foo: def __init__(self): detect_overridden(Foo, self) def method1(self): print("Hello foo") method2=f1 class Bar(Foo): def method1(self): print("Hello bar") method2=f1 # This is pointless but not an override # def method2(self): # pass b=Bar() f=Foo() 

Corre y da:

 ['method1'] [] 

En respuesta a la respuesta https://stackoverflow.com/a/9437273/1258307 , ya que todavía no tengo suficientes créditos para comentarlo, no funcionará en Python 3 a menos que reemplace im_func con __func__ y tampoco funcionará en Python 3.4 (y muy probablemente en adelante) ya que las funciones ya no tienen el atributo __func__ , solo métodos enlazados.

EDITAR: Aquí está la solución a la pregunta original (que funcionó en 2.7 y 3.4, y asumo todas las demás versiones intermedias):

  class Super: def __init__(self): if self.method.__code__ is Super.method.__code__: print('same') else: print('different') @classmethod def method(cls): pass class Sub1(Super): def method(self): print('hi') class Sub2(Super): pass Super() # should be same Sub1() # should be different Sub2() # should be same 

Y aquí está la salida:

 same different same 

Puede comparar lo que esté en el __dict__ de la clase con la función dentro del método que puede recuperar del objeto: la función “detectar_overriden” hace eso: el truco es pasar la “clase principal” por su nombre, tal como se hace en una llame a “super”; de lo contrario, no es fácil recuperar los atributos de la clase principal en lugar de los de la subclase:

 # -*- coding: utf-8 -*- from types import FunctionType def detect_overriden(cls, obj): res = [] for key, value in cls.__dict__.items(): if isinstance(value, classmethod): value = getattr(cls, key).im_func if isinstance(value, (FunctionType, classmethod)): meth = getattr(obj, key) if not meth.im_func is value: res.append(key) return res # Test and example class A(object): def __init__(self): print detect_overriden(A, self) def a(self): pass @classmethod def b(self): pass def c(self): pass class B(A): def a(self): pass #@classmethod def b(self): pass 

edite el código modificado para que funcione bien también con los métodos de clase: si detecta un método de clase en la clase principal, extrae la función subyacente antes de continuar.

– Otra forma de hacer esto, sin tener que codificar el nombre de la clase, sería seguir el orden de resolución del método class ( self.__class__ ) de la instancia (dado por el atributo __mro__ ) y buscar duplicados de los métodos y atributos definidos en Cada clase a lo largo de la cadena de herencia.

No estoy seguro de si esto es lo que estás buscando, pero me ayudó cuando buscaba una solución similar.

 class A: def fuzz(self): pass class B(A): def fuzz(self): super().fuzz() assert 'super' in B.__dict__['fuzz'].__code__.co_names