我正在使用NetworkX使用 python 进行图形模型项目。NetworkX 使用字典提供了简单而良好的功能:
import networkx as nx
G = nx.DiGraph() # a directed graph
G.add_edge('a', 'b')
print G['a'] # prints {'b': {}}
print G['b'] # prints {}
我想使用有向图,因为我正在编码具有方向的依赖项(在上面的示例中,我有以 'a' 为条件的 'b' 的封闭形式,而不是相反)。
对于给定的节点,我想找到该节点的前任。对于上面的例子,par('b') 应该返回 ['a']。NetworkX 确实有一个后继函数,它可以找到任何节点的子节点。显然,通过遍历所有节点并找到那些将“b”作为子节点的节点将起作用,但节点数量将是 Ω(n)(这对我的应用程序来说太贵了)。
我无法想象这个制作精良的包装中会遗漏这么简单的东西,但找不到任何东西。
一种有效的选择是存储图的有向和无向版本;所有无向边本质上都是通过添加两个有向边来实现的,因此可以获取相邻节点和子节点(这将是前任)之间的集合差异。
问题是我不确定包装现有 networkx DiGraph 和 Graph 类来实现这一点的最 Pythonic 方式。真的,我只想得到一个 PGraph 类,它的行为与 networkx DiGraph 类完全一样,但predecessors(node)
除了函数之外还有一个successors(node)
函数。
PGraph是否应该继承DiGraph并封装Graph(用于前辈功能)?那么我应该如何强制将所有节点和边添加到它包含的有向图和无向图中?我是否应该重新实现在 PGraph 中添加和删除节点和边的函数(以便在有向和无向版本中添加和删除它们)?我担心如果我错过了一些不起眼的东西,我以后会头疼,这可能并不意味着好的设计。
或者(请顺其自然True
)是否有一种简单的方法可以在 networkx.DiGraph 中获取节点的前任,而我完全错过了它?
非常感谢你的帮助。
编辑:
我认为这可以完成工作。PGraph 继承自有向图并封装了另一个有向图(这个颠倒了)。我已经覆盖了添加和删除节点和边的方法。
import networkx as nx
class PGraph(nx.DiGraph):
def __init__(self):
nx.DiGraph.__init__(self)
self.reversed_graph = nx.DiGraph()
def add_node(self, n, attr_dict=None, **attr):
nx.DiGraph.add_node(self, n, attr_dict, **attr)
self.reversed_graph.add_node(n, attr_dict, **attr)
def add_nodes_from(self, ns, attr_dict=None, **attr):
nx.DiGraph.add_nodes_from(self, ns, attr_dict, **attr)
self.reversed_graph.add_nodes_from(ns, attr_dict, **attr)
def add_edge(self, a, b, attr_dict=None, **attr):
nx.DiGraph.add_edge(self, a, b, attr_dict, **attr)
self.reversed_graph.add_edge(b, a, attr_dict, **attr)
def add_edges_from(self, es, attr_dict=None, **attr):
nx.DiGraph.add_edges_from(self, es, attr_dict, **attr)
self.reversed_graph.add_edges_from(es, attr_dict, **attr)
def remove_node(self, n):
nx.DiGraph.remove_node(self, n)
self.reversed_graph.remove_node(n)
def remove_nodes_from(self, ns):
nx.DiGraph.remove_nodes_from(self, ns)
self.reversed_graph.remove_nodes_from(ns)
def remove_edge(self, a, b):
nx.DiGraph.remove_edge(self, b, a)
self.reversed_graph.remove_edge(a, b)
def remove_edges_from(self, es):
nx.DiGraph.remove_edges_from(self, es)
self.reversed_graph.remove_edges_from([ (b,a) for a,b in es])
# the predecessors function I wanted
def predecessors(self, n):
return self.reversed_graph.successors(n)
您如何看待这个解决方案?它可能会使内存使用量翻倍,但我认为这是可以接受的。是不是太复杂了?这是好设计吗?