la classe de base de fonte de python de classe dérivée (ou plus pythonique façon d'étendre les classes)

StackOverflow https://stackoverflow.com/questions/3464061

Question

Je dois le paquet d'étendre python NetworkX et ajoutez quelques méthodes à la classe Graph pour mon besoin particulier

La façon dont je pensais à le faire est simplying dériver une nouvelle NewGraph de dire de classe, et en ajoutant les méthodes nécessaires.

Cependant, il existe plusieurs autres fonctions dans NetworkX et qui créent des objets Graph de retour (par exemple, générer un graphe aléatoire). Je maintenant besoin de transformer ces objets en objets Graph NewGraph afin que je puisse utiliser mes nouvelles méthodes.

Quelle est la meilleure façon de le faire? Ou dois-je faire face au problème d'une manière complètement différente?

Était-ce utile?

La solution

Si vous êtes ajouterez le comportement, et non en fonction des valeurs d'instance supplémentaires, vous pouvez attribuer à la __class__ de l'objet:

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>

Ceci est aussi proche d'un « cast » que vous pouvez obtenir en Python, et comme coulée en C, il ne doit pas être fait sans avoir réfléchi à la question. J'ai posté un exemple assez limité, mais si vous pouvez rester dans les contraintes (juste ajouter un comportement, pas nouvelle instance vars), cela pourrait aider à l'adresse de votre problème.

Autres conseils

Voici comment « comme par magie » remplacer une classe dans un module avec une sous-classe sur mesure sans toucher le module. Il est seulement quelques lignes supplémentaires d'une procédure de subclassing normale, et donc vous donne (presque) toute la puissance et la flexibilité des sous-classement en tant que bonus. Par exemple ce qui vous permet d'ajouter de nouveaux attributs, si vous le souhaitez.

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()

Jusqu'à présent, c'est exactement comme subclassing normal. Maintenant, nous devons accrocher cette sous-classe dans le module networkx afin que tous instanciation des résultats nx.Graph dans un objet NewGraph à la place. Voici ce qui se passe normalement lorsque vous instancier un objet nx.Graph avec 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

Nous allons remplacer nx.Graph.__new__ et le faire revenir NewGraph à la place. Dans ce document, nous appelons la méthode __new__ de object au lieu de la méthode __new__ de NewGraph, car celui-ci est juste une autre façon d'appeler la méthode que nous remplaçons, et se traduirait donc par récursion sans 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()

Dans la plupart des cas, cela est tout ce que vous devez savoir, mais il y a une chasse aux sorcières. Notre premier de la méthode __new__ ne concerne que nx.Graph, et non ses sous-classes. Par exemple, si vous appelez nx.gn_graph, qui renvoie une instance de nx.DiGraph, il aura aucun de nos extensions de fantaisie. Vous devez sous chacune des sous-classes de nx.Graph que vous souhaitez travailler et ajouter vos méthodes et les attributs nécessaires. En utilisant peut faciliter la constante étendre les sous-classes tout en respectant le principe Dry.

Bien que cet exemple peut sembler assez simple, cette méthode d'accrochage dans un module est difficile de généraliser d'une manière qui couvre tous les petits problèmes qui peuvent surgir. Je crois qu'il est plus facile de l'adapter au problème à portée de main. Par exemple, si la classe que vous êtes la crochetage en définit sa propre méthode de __new__ personnalisé, vous devez stocker avant de le remplacer, et appeler cette méthode au lieu de object.__new__.

Si une fonction est la création d'objets graphique, vous ne pouvez pas les transformer en objets NewGraph.

Une autre option est pour NewGraph est d'avoir un graphique plutôt que d'être un graphique. Vous déléguez les méthodes graphique pour l'objet graphique que vous avez, et vous pouvez envelopper un objet graphique dans un nouvel objet 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):
        ....

Pour votre cas simple, vous pouvez écrire votre sous-classe __init__ comme celui-ci et attribuer les pointeurs des structures de données graphique à vos données de sous-classe.

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)

Vous pouvez également utiliser la copie () ou deepcopy () dans les missions, mais si vous faites que vous pourriez aussi bien utiliser

G=MyGraph()
G.add_nodes_from(R)
G.add_edges_from(R.edges())

pour charger vos données de graphique.

vous pouvez simplement créer un nouveau dérivé de NewGraph objet Graph et ont la fonction __init__ comprennent quelque chose comme self.__dict__.update(vars(incoming_graph)) la première ligne, avant de définir vos propres propriétés. De cette façon, vous copiez essentiellement toutes les propriétés du Graph que vous avez sur un nouvel objet, dérivé de Graph, mais avec votre sauce.

class NewGraph(Graph):
  def __init__(self, incoming_graph):
    self.__dict__.update(vars(incoming_graph))

    # rest of my __init__ code, including properties and such

Utilisation:

graph = function_that_returns_graph()
new_graph = NewGraph(graph)
cool_result = function_that_takes_new_graph(new_graph)

Avez-vous essayé les gars [python] classe de base en fonte pour la classe dérivée

Je l'ai testé, et semble cela fonctionne. Je pense aussi cette méthode est un peu mieux que en dessous d'un car en dessous on n'exécute pas initialisation fonction de la fonction dérivée.

c.__class__ = CirclePlus
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top