Cast base class to derived class python (or more pythonic way of extending classes)
我需要扩展networkx python包,并为我的特殊需要向
我想这样做的方法是简单地派生一个新类,比如
但是,networkx中还有其他几个函数可以创建和返回
最好的方法是什么?或者我应该以完全不同的方式来解决这个问题?
如果只是添加行为,而不依赖于其他实例值,则可以将其分配给对象的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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) |
印刷品:
1 2 3 4 5 6 | 10 314.159265359 <__main__.Circle object at 0x00A0E270> 20 62.8318530718 <__main__.CirclePlus object at 0x00A0E270> |
这与您在Python中所能得到的"强制转换"非常接近,就像在C中强制转换一样,如果不考虑这个问题,就不可能做到这一点。我已经发布了一个相当有限的示例,但是如果您可以保持在约束范围内(只添加行为,不添加新的实例变量),那么这可能有助于解决您的问题。
下面介绍如何"神奇地"用定制的子类替换模块中的类,而不必触及模块。它只是普通子类化过程中的几行额外的代码,因此(几乎)为您提供了子类化的所有功能和灵活性作为奖励。例如,如果您愿意,这允许您添加新的属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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() |
到目前为止,这与普通的子类完全相同。现在,我们需要将这个子类挂接到
1 2 3 4 | 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 |
我们将更换
1 2 3 4 5 6 7 8 9 10 11 12 | 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() |
在大多数情况下,这是你所需要知道的,但有一个是正确的。我们对
尽管这个例子看起来足够简单,但是这种连接到模块中的方法很难用一种涵盖所有可能出现的小问题的方式进行概括。我相信根据手头的问题调整它会更容易。例如,如果要挂接的类定义了自己的自定义
在定义自己的属性之前,您可以简单地创建一个从
1 2 3 4 5 | class NewGraph(Graph): def __init__(self, incoming_graph): self.__dict__.update(vars(incoming_graph)) # rest of my __init__ code, including properties and such |
用途:
1 2 3 | graph = function_that_returns_graph() new_graph = NewGraph(graph) cool_result = function_that_takes_new_graph(new_graph) |
对于简单的例子,您也可以这样编写子类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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(),但如果您这样做,您也可以使用
1 2 3 | G=MyGraph() G.add_nodes_from(R) G.add_edges_from(R.edges()) |
加载图形数据。
如果函数正在创建图形对象,则不能将其转换为newgraph对象。
newgraph的另一个选择是拥有一个图而不是一个图。将图形方法委托给您拥有的图形对象,然后可以将任何图形对象包装为新的图形对象:
1 2 3 4 5 6 7 8 9 10 | 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函数。
1 | c.__class__ = CirclePlus |