Gussbasisklasse abgeleiteten Klasse Python (oder mehr pythonic Weise Klassen erstreckt)
-
27-09-2019 - |
Frage
Ich brauche das NetworkX Python-Paket zu erweitern und ein paar Methoden, um die Graph
Klasse für meine besondere Notwendigkeit
So wie ich dachte daran, dies zu tun ist simplying eine neue Klasse sagen NewGraph
abzuleiten, und das Hinzufügen der erforderlichen Methoden.
Es gibt jedoch mehrere andere Funktionen in NetworkX die Erstellung und Rückkehr Graph
Objekte (zum Beispiel eine Zufallsgraphen erzeugen). Ich brauche jetzt diese Graph
Objekte verwandeln sich in NewGraph
Objekte, so dass ich meine neue Methoden verwenden können.
Was ist der beste Weg, dies zu tun? Oder soll ich das Problem auf eine ganz andere Art und Weise anpacken?
Lösung
Wenn Sie nur das Verhalten Zugabe werden, und in Abhängigkeit nicht auf zusätzliche Instanz Werten, können Sie auf das __class__
Objekt zuweisen:
from math import pi
class Circle(object):
def __init__(self, radius):
self.radius = radius
def area(self):
return pi * self.radius**2
class CirclePlus(Circle):
def diameter(self):
return self.radius*2
def circumference(self):
return self.radius*2*pi
c = Circle(10)
print c.radius
print c.area()
print repr(c)
c.__class__ = CirclePlus
print c.diameter()
print c.circumference()
print repr(c)
Prints:
10
314.159265359
<__main__.Circle object at 0x00A0E270>
20
62.8318530718
<__main__.CirclePlus object at 0x00A0E270>
Das ist so nah an einem „Guss“, wie Sie in Python zu bekommen, und wie in C Gießen, ist es nicht ohne dem Thema einige Gedanken zu tun. Ich habe ein ziemlich begrenztes Beispiel geschrieben, aber wenn man in den Grenzen bleiben kann (nur Verhalten hinzufügen, keine neue Instanz VARs), dann könnte dies helfen Adresse Ihres Problem.
Andere Tipps
Hier ist, wie man „magische Weise“ eine Klasse in einem Modul mit einer maßgeschneiderten Unterklasse ersetzen, ohne das Modul zu berühren. Es ist nur ein paar zusätzlichen Zeilen von einem normalen Subklassifizieren Verfahren, und deshalb gibt Ihnen (fast) die ganze Kraft und Flexibilität als Bonus von Subklassen. Zum Beispiel können Sie so neue Attribute hinzufügen, wenn Sie möchten.
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()
Bisher ist dies genau wie normale Subklassifizieren. Jetzt müssen wir diese Unterklasse in die networkx
Modul anschließen, so dass alle Instanziierung nx.Graph
Ergebnisse in einem NewGraph
Objekt statt. Hier ist, was normalerweise passiert, wenn Sie ein Objekt nx.Graph
mit nx.Graph()
instanziiert
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
Wir werden nx.Graph.__new__
ersetzen und es NewGraph
stattdessen machen zurückzukehren. Darin wir die __new__
Methode der object
anstelle des __new__
Methode der NewGraph
nennen, da diese nur eine andere Art von Aufruf der Methode ist sind zu ersetzen wir, und in endlosen Rekursion deshalb führen würde.
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()
In den meisten Fällen ist dies alles, was Sie wissen müssen, aber es gibt eine Gotcha. Unser übergeordnetes der Obwohl dieses Beispiel einfach genug zu sein scheint, diese Methode in einem Modul Einhaken ist schwer, in einer Art und Weise zu verallgemeinern, dass deckt all die kleinen Probleme, die auftauchen können. Ich glaube, es ist einfacher, nur Schneider es auf der Hand, um das Problem. Zum Beispiel, wenn die Klasse Sie sind Einhaken in definiert seine eigene individuelle __new__
Methode wirkt sich nur auf nx.Graph
, nicht ihre Unterklassen. Zum Beispiel, wenn Sie nx.gn_graph
nennen, die eine Instanz von nx.DiGraph
zurückkehrt, wird es keine unserer Phantasie Erweiterungen haben. Sie müssen jede der Unterklassen von nx.Graph
Unterklasse, mit dem Sie arbeiten wollen und Ihre erforderlichen Methoden und Attribute hinzufügen. Mix-ins können erleichtern konsequent die Unterklassen erweitert, während das href="http://en.wikipedia.org/wiki/Don't_repeat_yourself" rel="nofollow noreferrer"> DRY Prinzip
__new__
Methode, müssen Sie es speichern, bevor sie, und rufen Sie diese Methode anstelle von object.__new__
zu ersetzen.
Wenn eine Funktion schafft Graph-Objekte, können Sie sie nicht in NewGraph drehen Objekte.
Eine weitere Option für NewGraph ist ein Diagramm zu haben, anstatt einen Graph sein. Sie delegieren die Graph Methoden auf das Graph-Objekt Sie haben, und Sie können jede Graph-Objekt in ein neues NewGraph Objekt wickeln:
class NewGraph:
def __init__(self, graph):
self.graph = graph
def some_graph_method(self, *args, **kwargs):
return self.graph.some_graph_method(*args, **kwargs)
#.. do this for the other Graph methods you need
def my_newgraph_method(self):
....
Für Ihren einfachen Fall könnten Sie auch Ihre Unterklasse __init__
so schreiben und die Zeiger von den Graph-Datenstrukturen der Unterklasse Daten zuweisen.
from networkx import Graph
class MyGraph(Graph):
def __init__(self, graph=None, **attr):
if graph is not None:
self.graph = graph.graph # graph attributes
self.node = graph.node # node attributes
self.adj = graph.adj # adjacency dict
else:
self.graph = {} # empty graph attr dict
self.node = {} # empty node attr dict
self.adj = {} # empty adjacency dict
self.edge = self.adj # alias
self.graph.update(attr) # update any command line attributes
if __name__=='__main__':
import networkx as nx
R=nx.gnp_random_graph(10,0.4)
G=MyGraph(R)
Sie können auch copy () oder deep () in den Zuordnungen verwenden, aber wenn Sie tun, dass Sie könnte genauso gut verwenden
G=MyGraph()
G.add_nodes_from(R)
G.add_edges_from(R.edges())
Ihre Diagrammdaten laden.
Sie könnten einfach einen neuen NewGraph
erstellen aus Graph
Objekt abgeleitet und haben die __init__
Funktion umfasst so etwas wie self.__dict__.update(vars(incoming_graph))
als die erste Zeile, bevor Sie Ihre eigenen Eigenschaften definieren. Auf diese Weise kopieren Sie im Grunde alle die Eigenschaften aus der Graph
Sie auf ein neues Objekt haben, von Graph
abgeleitet, aber mit Ihrer speziellen Sauce.
class NewGraph(Graph):
def __init__(self, incoming_graph):
self.__dict__.update(vars(incoming_graph))
# rest of my __init__ code, including properties and such
Verbrauch:
graph = function_that_returns_graph()
new_graph = NewGraph(graph)
cool_result = function_that_takes_new_graph(new_graph)
Haben Sie Jungs versucht, [Python] Gussbasisklasse abgeleitete Klasse
Ich habe es getestet und scheint es funktioniert. Ich denke, auch diese Methode ist etwas besser als unter einem da unterhalb einer nicht ausgeführt init Funktion der abgeleiteten Funktion.
c.__class__ = CirclePlus