sobrecargar - ¿Cómo detectar la sobrecarga de métodos en subclases en python?
sobrecargar metodos en python (5)
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?
En respuesta a la respuesta https://.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
No estoy seguro de si esto es lo que estás buscando, pero me ayudó cuando estaba buscando 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
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'']
[]
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 "detect_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.
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