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?

È stato utile?

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
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top