将基类转换为派生类python(或扩展类的更多pythonic方法)

我需要扩展Networkx python包,并为我的特殊需要添加一些Graph类的方法

我想这样做的方法是简化派生一个新的类NewGraph ,并添加所需的方法。

然而networkx中还有其他一些函数可以创build和返回Graph对象(例如生成一个随机图)。 我现在需要将这些Graph对象变成NewGraph对象,以便我可以使用我的新方法。

这样做的最好方法是什么? 还是应该以完全不同的方式来解决问题呢?

如果您只是添加行为,而不依赖于其他实例值,则可以分配给该对象的__class__

 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) 

打印:

 10 314.159265359 <__main__.Circle object at 0x00A0E270> 20 62.8318530718 <__main__.CirclePlus object at 0x00A0E270> 

这与Python中的“cast”非常接近,并且像在C中进行投射一样,在没有考虑到这个问题的情况下就不要这样做。 我已经发布了一个相当有限的例子,但是如果你能保持约束(只是添加行为,没有新的实例variables),那么这可能有助于解决你的问题。

以下是如何“神奇地”用一个定制的子类replace一个模块中的类,而不用触摸模块。 这只是正常子类化过程中的一些额外的行,因此可以提供(几乎)所有子类的强大function和灵活性。 例如,如果你愿意,这可以让你添加新的属性。

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

到目前为止,这完全像正常的子类。 现在我们需要把这个子类挂接到networkx模块中,以便networkx所有实例nx.Graph一个NewGraph对象。 下面是当你用nx.Graph()实例化一个nx.Graph对象时通常发生的事情

 1. nx.Graph .__ new __(nx.Graph)被调用
 2.如果返回的对象是nx.Graph的子类, 
   在对象上调用__init__
 3.该对象作为实例返回

我们将取代nx.Graph.__new__并使其返回NewGraph 。 在这里,我们调用object__new__方法,而不是__new__方法,因为后者只是调用我们要replace的方法的另一种方法,因此会导致无穷recursion。

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

在大多数情况下,这是所有你需要知道的,但有一个问题。 我们重写__new__方法只影响nx.Graph ,而不是它的子类。 例如,如果您调用返回nx.gn_graph的实例的nx.DiGraph ,它将没有我们的奇特扩展。 你需要nx.Graph你希望使用的nx.Graph每个子类,并添加你需要的方法和属性。 使用混合可能会更容易一致地扩展子类,同时遵守DRY原则。

虽然这个例子可能看起来很直截了当,但是这种钩入模块的方法很难概括,涵盖了所有可能出现的小问题。 我相信只是针对手头的问题量身订做就更容易了。 例如,如果你正在钩入的类定义了自己的自定义__new__方法,则需要在replace之前存储它,并调用此方法而不是object.__new__

如果一个函数创buildGraph对象,则不能将它们转换为NewGraph对象。

另一个select是为NewGraph是有一个graphics,而不是一个graphics。 您可以将Graph方法委托给Graph对象,并且可以将任何Graph对象包装到新的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): .... 

对于你的简单情况,你也可以像这样写你的子类__init__ ,并将Graph数据结构中的指针分配给你的子类数据。

 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) 

你也可以在作业中使用copy()或deepcopy(),但如果你这样做,你也可以使用它

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

加载你的graphics数据。

您可以简单地创build一个从Graph对象派生的新的NewGraph ,并在定义自己的属性之前,将__init__函数包括像self.__dict__.update(vars(incoming_graph))作为第一行。 通过这种方式,您基本上可以将Graph所有属性复制到一个从Graph派生的新对象上,但是使用特殊的酱料。

 class NewGraph(Graph): def __init__(self, incoming_graph): self.__dict__.update(vars(incoming_graph)) # rest of my __init__ code, including properties and such 

用法:

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

你们有没有试过[Python]将基类投射到派生类

我已经testing过了,似乎有效。 另外我觉得这个方法比下面的方法好一点,因为下面的方法不执行派生函数的init函数。

 c.__class__ = CirclePlus