la classe de base de fonte de python de classe dérivée (ou plus pythonique façon d'étendre les classes)
-
27-09-2019 - |
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?
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