calco base per pitone classe derivata (o modo più divinatorio di estendere classi)
-
27-09-2019 - |
Domanda
Ho bisogno di estendere il pacchetto python NetworkX e aggiungere alcuni metodi alla classe Graph
per la mia particolare necessità
Il modo in cui ho pensato di fare questo è simplying derivare una nuova classe di NewGraph
diciamo, e aggiungendo i metodi richiesti.
Tuttavia, esistono svariate altre funzioni in NetworkX che creano e oggetti ritorno Graph
(ad esempio generare un grafo casuale). Ora ho bisogno di trasformare questi oggetti in Graph
NewGraph
oggetti in modo che posso usare i miei nuovi metodi.
Qual è il modo migliore di fare questo? O dovrei affrontare il problema in un modo completamente diverso?
Soluzione
Se sono solo aggiungendo il comportamento, e non a seconda dei valori ulteriore istanza, è possibile assegnare alla __class__
dell'oggetto:
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)
Stampe:
10
314.159265359
<__main__.Circle object at 0x00A0E270>
20
62.8318530718
<__main__.CirclePlus object at 0x00A0E270>
Questo è il più vicino ad un "cast" come si può ottenere in Python, e come gettare in C, non deve essere fatto senza dare la questione qualche pensiero. Ho postato un esempio abbastanza limitata, ma se si può rimanere entro i limiti (solo aggiungere comportamento, non nuova istanza vars), allora questo indirizzo potrebbe aiutare il vostro problema.
Altri suggerimenti
Ecco come "magicamente" sostituire una classe in un modulo con una sottoclasse misura senza toccare il modulo. E 'solo poche righe in più di una normale procedura di sottoclasse, e pertanto è (quasi) offre tutta la potenza e la flessibilità di sottoclassi come bonus. Per esempio questo consente di aggiungere nuovi attributi, se lo si desidera.
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()
Finora questo è esattamente come sottoclassi normale. Ora dobbiamo agganciare questa sottoclasse nel modulo networkx
modo che tutti istanziazione dei risultati nx.Graph
in un oggetto NewGraph
invece. Ecco cosa accade normalmente quando si crea un'istanza di un oggetto 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
sostituirà nx.Graph.__new__
e farla tornare NewGraph
invece. In esso, si chiama il metodo di __new__
object
invece del metodo __new__
di NewGraph
, perché quest'ultimo è solo un altro modo di chiamare il metodo che stiamo sostituendo, e sarebbe quindi tradursi in ricorsione infinita.
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()
Nella maggior parte dei casi questo è tutto quello che dovete sapere, ma c'è un Gotcha. Il nostro primario del metodo __new__
riguarda solo nx.Graph
, non le sue sottoclassi. Ad esempio, se si chiama nx.gn_graph
, che restituisce un'istanza di nx.DiGraph
, avrà nessuna delle nostre estensioni di fantasia. È necessario sottoclasse ciascuna delle sottoclassi di nx.Graph
che si desidera lavorare con e aggiungere i vostri metodi e attributi richiesti. Utilizzando mix-ins può rendere più facile per sempre estendere le sottoclassi, mentre obbedendo linea di principio la SECCO .
Anche se questo esempio può sembrare abbastanza semplice, questo metodo di aggancio in un modulo è difficile generalizzare in un modo che copre tutti i piccoli problemi che possono sorgere. Credo che sia più facile da solo su misura al problema in questione. Per esempio, se la classe voi sono aggancio nella definisce il proprio metodo __new__
personalizzato, è necessario memorizzare prima di sostituirla, e chiamare questo metodo al posto di object.__new__
.
Se una funzione è la creazione di oggetti grafico, non è possibile trasformarli in NewGraph oggetti.
Un'altra opzione è per NewGraph è quello di avere un grafico piuttosto che essere un grafico. Si delega i metodi grafico per l'oggetto grafico che hai, e si può avvolgere qualsiasi oggetto grafico in un nuovo oggetto NewGraph:
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):
....
Per il vostro caso semplice si potrebbe anche scrivere il vostro __init__
sottoclasse di questo tipo e assegnare i puntatori da strutture dati grafico al tuo dati di sottoclasse.
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)
Si potrebbe anche usare copy () o deepcopy () nelle assegnazioni, ma se si sta facendo che si potrebbe anche usare
G=MyGraph()
G.add_nodes_from(R)
G.add_edges_from(R.edges())
per caricare i dati del grafico.
Si potrebbe semplicemente creare una nuova NewGraph
derivato da oggetto Graph
e hanno la funzione di __init__
comprendono qualcosa come self.__dict__.update(vars(incoming_graph))
come la prima linea, prima di definire le proprie proprietà. In questo modo che, fondamentalmente, di copiare tutte le proprietà dalla Graph
si dispone su un nuovo oggetto, derivato da Graph
, ma con la vostra salsa speciale.
class NewGraph(Graph):
def __init__(self, incoming_graph):
self.__dict__.update(vars(incoming_graph))
# rest of my __init__ code, including properties and such
Utilizzo:
graph = function_that_returns_graph()
new_graph = NewGraph(graph)
cool_result = function_that_takes_new_graph(new_graph)
Hai provato ragazzi [Python] classe base in fusione di classe derivata
ho provato, e sembra che funziona. Anche io penso che questo metodo è po 'meglio di sotto di un dato al di sotto di uno non eseguire init funzione della funzione derivata.
c.__class__ = CirclePlus