Aquí se explica cómo reemplazar "mágicamente" una clase en un módulo con una subclase hecha a medida sin tocar el módulo. Son solo unas pocas líneas extra de un procedimiento de subclases normal, y por lo tanto te da (casi) todo el poder y la flexibilidad de la subclasificación como bonificación. Por ejemplo, esto le permite agregar nuevos atributos, si lo desea.
import networkx as nx
class NewGraph(nx.Graph):
def __getattribute__(self, attr):
"This is just to show off, not needed"
print "getattribute %s" % (attr,)
return nx.Graph.__getattribute__(self, attr)
def __setattr__(self, attr, value):
"More showing off."
print " setattr %s = %r" % (attr, value)
return nx.Graph.__setattr__(self, attr, value)
def plot(self):
"A convenience method"
import matplotlib.pyplot as plt
nx.draw(self)
plt.show()
Hasta ahora esto es exactamente como una subclase normal. Ahora tenemos que enganchar esta subclase en el módulo networkx
para que todas las instancias de nx.Graph
tengan como resultado un objeto NewGraph
. Esto es lo que normalmente ocurre cuando se instancia un objeto nx.Graph
con nx.Graph()
1. nx.Graph.__new__(nx.Graph) is called
2. If the returned object is a subclass of nx.Graph,
__init__ is called on the object
3. The object is returned as the instance
Vamos a sustituir nx.Graph.__new__
y hacerlo volver NewGraph
lugar. En él, llamamos al método __new__
de object
en lugar del método __new__
de NewGraph
, porque este último es simplemente otra forma de llamar al método que estamos reemplazando, y por lo tanto daría como resultado una recursión sin fin.
def __new__(cls):
if cls == nx.Graph:
return object.__new__(NewGraph)
return object.__new__(cls)
# We substitute the __new__ method of the nx.Graph class
# with our own.
nx.Graph.__new__ = staticmethod(__new__)
# Test if it works
graph = nx.generators.random_graphs.fast_gnp_random_graph(7, 0.6)
graph.plot()
En la mayoría de los casos, esto es todo lo que necesita saber, pero hay una gotcha. Nuestra anulación del método __new__
solo afecta a nx.Graph
, no a sus subclases. Por ejemplo, si llama al nx.gn_graph
, que devuelve una instancia de nx.DiGraph
, no tendrá ninguna de nuestras extensiones de lujo.Debe subclasificar cada una de las subclases de nx.Graph
con las que desea trabajar y agregar los métodos y atributos necesarios. Usar mix-ins puede facilitar la extensión consistente de las subclases al tiempo que se cumple el principio DRY.
Aunque este ejemplo puede parecer bastante directo, este método de enganche en un módulo es difícil de generalizar de una manera que cubra todos los pequeños problemas que puedan surgir. Creo que es más fácil adaptarlo al problema en cuestión. Por ejemplo, si la clase en la que está enganchando define su propio método personalizado __new__
, debe almacenarlo antes de reemplazarlo y llamar a este método en lugar de object.__new__
.
Ok, entonces, ¿qué sucede cuando _necesitas agregar variables? –
Puede agregar/establecer variables de instancia en tiempo de ejecución. Sin embargo, ten en cuenta que no te confundas con la variable de instancia agregada por CirclePlus __init__ que olvidaste agregar porque este método de conversión omite __init__, supongo. Por cierto, dado que el sistema de tipos de Python se puede anular, este método de conversión no siempre funcionará. –
Si encuentra que también necesita agregar variables de instancia, entonces creo que está yendo más allá del dominio del código que se puede mantener: tiempo para repensar su diseño, probablemente utilizando alguna forma de contención y/o delegación. – PaulMcG