拓冰建站拓冰建站
首页 / 资讯中心 / 正文

NetworkX 1.X 到 2.0 迁移指南:视图/迭代器 API、属性访问与函数命名空间的全面升级

NetworkX 1.X 到 2.0 迁移指南视图/迭代器 API、属性访问与函数命名空间的全面升级【免费下载链接】networkxNetwork Analysis in Python项目地址: https://gitcode.com/gh_mirrors/ne/networkx本指南以仓库 doc/release/migration_guide_from_1.x_to_2.0.rst 为骨架结合当前仓库源码整理而成。NetworkX 2.0 是一次破坏性的大版本升级基础图类Graph / DiGraph / MultiGraph / MultiDiGraph的许多方法签名、返回类型与属性访问方式发生了根本性变化。阅读本文后你将掌握 2.0 视图/迭代器 API 的正确用法、G.node/G.edge的替代方案、set_node_attributes等函数的参数重排、以及如何编写一套同时兼容 1.X 与 2.0 的迁移代码。一、2.0 的核心变化从容器返回到视图/迭代器报告随着 NetworkX 2.0 的发布项目转向了view/iterator reporting API视图/迭代器报告 API。大量原本返回列表或字典的方法改为返回视图view视图的设计灵感来自 Python 的 dictionary views。具体而言原本返回容器的 API 现在返回视图NodeView、EdgeView、DegreeView原本返回迭代器的 API如nodes_iter、edges_iter则被直接移除创建新图的方法G.subgraph/edge_subgraph/reverse/to_directed/to_undirected在数据复制的深度上发生了改变且多数新增了以视图view方式创建而非复制数据的选项图属性G.node与G.edge被移除改用G.nodes[n]与G.edges[u, v]自环相关方法以及add_path/add_star/add_cycle从图方法迁移为 networkx 命名空间下的函数。设计者预期这些变化会破坏部分旧代码但刻意让它们在抛出异常的方式上失败如TypeError: unhashable type: dict以便开发者能立刻意识到代码已被破坏而不是静默产生错误结果。在 networkx/classes/reportviews.py 的模块文档字符串中可以完整看到视图类家族的定位视图是快速创建、只读、可多次迭代的容器并且是活的live——它们实时反映图的变化节点与边视图支持类集合set-like的成员测试与集合运算支持映射式查询G.nodes[n]返回节点属性字典G.edges[u, v]返回边属性字典通过.data(...)支持按数据过滤的迭代通过list(...)/dict(...)转换为具体容器。需要特别留意的是视图在迭代期间不应修改底层图结构与 Python dict 的约束一致否则会引发运行时错误视图本身则可以反复迭代。二、NodeViewG.nodes取代G.node与nodes_iter2.1 基本用法G.nodes出于向后兼容G.nodes()也可用现在返回一个类似字典的NodeView而G.nodes_iter()已被移除 import networkx as nx G nx.complete_graph(5) G.nodes # for backward compatibility G.nodes() works as well NodeView((0, 1, 2, 3, 4))可以直接迭代G.nodes或G.nodes() for node in G.nodes: ... print(node) 0 1 2 3 4如果需要一个节点列表使用 Python 内置的list函数 list(G.nodes) [0, 1, 2, 3, 4]2.2 集合运算与字典式查询G.nodes具有集合特性可以参与集合运算同时它又是字典式的可以用G.nodes[n][weight]查询节点数据 H nx.Graph() H.add_nodes_from([1, networkx, 2.0]) G.nodes H.nodes # finding common nodes in 2 graphs {1} # union of nodes in 2 graphs G.nodes | H.nodes {0, 1, 2, 3, 4, networkx, 2.0}节点数据则可以通过调用接口或数据视图访问。除了字典式视图自带的keys/values/itemsG.nodes还提供数据视图G.nodes.data(weight) G.add_node(1, time5pm) list(G.nodes(dataTrue)) [(0, {}), (1, {time: 5pm}), (2, {}), (3, {}), (4, {})] list(G.nodes.data(time, defaultNot Available)) [(0, Not Available), (1, 5pm), (2, Not Available), (3, Not Available), (4, Not Available)]源码层面nodes在 networkx/classes/graph.py 中被实现为cached_property返回NodeView调用形态G.nodes(data...)则产生NodeDataView迭代(node, data)对且不提供集合运算。NodeDataView的详细行为定义在 networkx/classes/reportviews.py使用dataTrue时数据视图携带完整属性字典可写使用datacolor时仅携带单个属性值default参数可为缺失属性提供默认值。2.3G.node的去向图属性G.node已被移除其功能完全转移到G.nodes查询节点属性G.node[n]→G.nodes[n]遍历节点属性对G.node.items()→G.nodes(dataTrue)仅遍历节点for n in G.node:→for n in G:对图对象直接迭代节点见 networkx/classes/graph.py 的__iter__。需要说明的是为了帮助旧代码平滑过渡v2.x 中保留了G.node作为指向G.nodes的过渡属性transition pointer项目曾计划在 v3.x 中将其彻底移除——升级到 2.0 后应尽快改用G.nodes。2.4 危险的属性字典直赋操作将节点属性字典直接从一张图复制到另一张图可能破坏节点数据结构。下列写法在 v1.x 中碰巧能工作即使n不是G中的节点时可能出错在 v2.x 中则会直接报错 # dangerous in v1.x, not allowed in v2.x G.node[n] H.node[n] # 会报错请改用更安全的版本 G.nodes[n].update(H.nodes[n]) # works in v2.x三、EdgeViewG.edges取代G.edge与edges_iterG.edges现在返回EdgeView而非边列表同样支持集合运算 G.edges # for backward compatibility G.edges() works as well EdgeView([(0, 1), (0, 2), (0, 3), (0, 4), (1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]) list(G.edges) [(0, 1), (0, 2), (0, 3), (0, 4), (1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]边数据通过G.edges[u, v]查询取代G.edge[u][v]。EdgeView的可调用形态G.edges(dataweight, default1)会生成EdgeDataView根据data与default参数分别迭代二元组(u, v)dataFalse默认、三元组(u, v, datadict)dataTrue或(u, v, datadict.get(data, default))nbunch参数可将迭代范围限制为与某组节点相邻的边。这些细节均定义在 networkx/classes/reportviews.py。EdgeDataView只支持迭代与成员测试不支持集合运算对无向图而言(0, 1)与(1, 0)并不是唯一表示因此做集合运算时需保证参与比较的集合使用唯一的边表示。对G.edge属性的替代迁移指南给出的通用建议是——把所有G.edge替换为G.adj。G.adj在 v1 中就是G.edge在 v2 中继续可用因此在两代版本间都能工作。在 networkx/classes/graph.py 中adj是返回AdjacencyView的cached_property文档明确说明其结构为以节点为键、邻居字典为值的只读 dict 式结构G.adj[3][2][color] blue可以设置边(3, 2)的颜色属性。四、DegreeViewG.degree与in_degree/out_degreeG.degree返回DegreeView。与其他视图相比它的字典性更弱按(node, degree)二元组迭代不提供keys/values/items/get方法但支持G.degree[n]的单项查询。需要以节点为键的度数字典时dict(G.degree)即可生成 G.degree # for backward compatibility G.degree() works as well DegreeView({0: 4, 1: 4, 2: 4, 3: 4, 4: 4}) G.degree([1, 2, 3]) DegreeView({1: 4, 2: 4, 3: 4}) list(G.degree([1, 2, 3])) [(1, 4), (2, 4), (3, 4)] dict(G.degree([1, 2, 3])) {1: 4, 2: 4, 3: 4} list(G.degree) [(0, 4), (1, 4), (2, 4), (3, 4), (4, 4)] dict(G.degree) {0: 4, 1: 4, 2: 4, 3: 4, 4: 4}nbunch参数可以将视图限制为只包含指定节点如上面的G.degree([1, 2, 3])。单个节点的度数用G.degree[node]计算。有向图的in_degree/out_degree也做了同样改造。若只想取度数数值有几种风格可选以DiGraph的in_degree为例out_degree与degree同理 DG nx.DiGraph() DG.add_weighted_edges_from([(1, 2, 0.5), (3, 1, 0.75)]) deg DG.in_degree # sets up the view [d for n, d in deg] # gets all nodes degree values [1, 1, 0] (d for n, d in deg) # iterator over degree values generator object genexpr ... [deg[n] for n in [1, 3]] # using lookup for only some nodes [1, 0]以下写法在 v1 与 v2 中都能工作 for node, in_deg in dict(DG.in_degree).items(): # works for nx1 and nx2 ... print(node, in_deg) 1 1 2 1 3 0 dict(DG.in_degree([1, 3])).values() # works for nx1 and nx2 dict_values([1, 0]) list(d for n, d in DG.in_degree([1, 3])) # DG.in_degree(nlist) 创建仅含 nlist 中节点的受限视图 [1, 0]若追求极致性能还可以直接借助底层邻接结构pred/adj这种方式同样不受 API 变更影响 [len(nbrs) for n, nbrs in DG.pred.items()] # probably slightly fastest for all nodes [1, 1, 0] [len(DG.pred[n]) for n in [1, 3]] # probably slightly faster for only some nodes [1, 0]DegreeView的完整语义见 networkx/classes/reportviews.py有向图中G.degree同时统计入边与出边G.in_degree/G.out_degree只统计单一方向还支持加权度G.degree(weightattr_name)度数视图不提供集合运算需要集合特性时请用NodeView。五、邻居迭代G.neighbors与有向图视图如果n是G中的节点G.neighbors(n)返回迭代器不再是列表 n 1 G.neighbors(n) dict_keyiterator object at ... list(G.neighbors(n)) [0, 2, 3, 4]DiGraph的视图与Graph类似但拥有更多方法in_edges/out_edges/successors/predecessors/in_degree/out_degree D nx.DiGraph() D.add_edges_from([(1, 2), (2, 3), (1, 3), (2, 4)]) D.nodes NodeView((1, 2, 3, 4)) list(D.nodes) [1, 2, 3, 4] D.edges OutEdgeView([(1, 2), (1, 3), (2, 3), (2, 4)]) list(D.edges) [(1, 2), (1, 3), (2, 3), (2, 4)] D.in_degree[2] 1 D.out_degree[2] 2 D.in_edges InEdgeView([(1, 2), (2, 3), (1, 3), (2, 4)]) list(D.in_edges()) [(1, 2), (2, 3), (1, 3), (2, 4)] D.out_edges(2) OutEdgeDataView([(2, 3), (2, 4)]) list(D.out_edges(2)) [(2, 3), (2, 4)] D.in_degree InDegreeView({1: 0, 2: 1, 3: 2, 4: 1}) list(D.in_degree) [(1, 0), (2, 1), (3, 2), (4, 1)] D.successors(2) dict_keyiterator object at ... list(D.successors(2)) [3, 4] D.predecessors(2) dict_keyiterator object at ... list(D.predecessors(2)) [1]注意D.edges默认是OutEdgeView出边视图D.in_edges提供入边视图out_edges(2)只返回节点 2 的出边。同样的变更适用于MultiGraph与MultiDiGraph唯一的差别是多重图边默认迭代为(u, v, key)三元组可用G.edges(keysFalse)取得二元组见 networkx/classes/reportviews.py。六、set_node_attributes/set_edge_attributes参数顺序对调set_node_attributes与set_edge_attributes的参数顺序发生了变化name与values的位置互换且name现在默认为None。旧签名(graph, name, value)改为(graph, value, nameNone)。新签名允许省略name直接把节点到属性字典的字典传给values。在源码 networkx/classes/function.py 中set_node_attributes(G, values, nameNone)的文档字符串直接给出了警告Warning: The call order of argumentsvaluesandnameswitched between v1.x v2.x.values的行为规则如下若非字典则视为单个属性值应用到G的所有节点属性名取name例如nx.set_node_attributes(G, labels, labels)若是字典则以节点为键映射到属性值或属性字典若传入字典的字典外层字典按节点映射到内层属性字典此时可省略name。旧代码若不改会立刻失败 G nx.Graph([(1, 2), (1, 3)]) nx.set_node_attributes(G, label, {1: one, 2: two, 3: three}) # 新版本报错 nx.set_edge_attributes(G, label, {(1, 2): path1, (2, 3): path2}) # 新版本报错在 v2.0 中会触发TypeError: unhashable type: dict。重构为显式关键字参数即可 G nx.Graph([(1, 2), (1, 3)]) nx.set_node_attributes(G, namelabel, values{1: one, 2: two, 3: three}) nx.set_edge_attributes(G, namelabel, values{(1, 2): path1, (2, 3): path2})这是迁移中最简单的技巧显式写出关键字参数名。它向后兼容、保证参数正确传递无论参数顺序如何因而也是后面双版本兼容代码章节的核心手段。get_node_attributes/get_edge_attributes同样受此影响。七、方法迁移从图类到主命名空间的函数以下方法从基础图类移动到了 networkx 主命名空间v1.x 图方法v2.x 命名空间函数G.add_pathnx.add_path(G, ...)G.add_starnx.add_star(G, ...)G.add_cyclenx.add_cycle(G, ...)G.number_of_selfloopsnx.number_of_selfloops(G)G.nodes_with_selfloopsnx.nodes_with_selfloops(G)G.selfloop_edgesnx.selfloop_edges(G)这些函数的当前实现位于 networkx/classes/function.pyadd_star/add_path/add_cycle分别在 function.py、function.py、function.pynodes_with_selfloops在 function.pyselfloop_edges(G, dataFalse, keysFalse, defaultNone)在 function.pynumber_of_selfloops在 function.py。为了向后兼容v2.x 中保留了这些方法的废弃版本deprecated methods——调用G.add_path(...)仍会工作但会收到弃用警告。注意这些函数采用nx.add_path(G, nodes, ...)的显式图参数风格与旧的G.add_path(nodes, ...)调用方式不同。八、GraphViews 与数据复制语义as_view、fresh_copy、root_graphG.subgraph/edge_subgraph/reverse/to_directed/to_undirected创建新图的方式发生了改变多数支持as_viewTrue选项以创建视图而非复制数据且默认数据复制深度也可能不同。在 networkx/classes/graph.py 中可以看到to_directed(self, as_viewFalse)graph.pyto_undirected(self, as_viewFalse)graph.pysubgraph(self, nodes)graph.py。DiGraph/MultiGraph/MultiDiGraph也有对应实现如 networkx/classes/digraph.py、networkx/classes/multigraph.py、networkx/classes/multigraph.py。视图带来两个重要推论不要假设G.__class__()能创建与G相同类型的新图实例。当G是 SubGraph、ReversedGraph 等视图时__class__的调用签名与基类不同。v2.x 中应使用G.fresh_copy()创建正确类型的空图再填充节点与边。视图可以嵌套视图的视图的视图……若要追溯到链条最底层的原始图使用G.root_graph。但需注意root_graph返回的图可能与视图的有向/无向类型不同例如无向图的reverse视图其根图仍然是无向的使用时务必确认类型。九、topological_sort的参数收窄topological_sort不再接受reverse与nbunch参数。当前实现位于 networkx/algorithms/dag.py是一个纯生成器函数且对无向图抛出NetworkXError、对含环图抛出NetworkXUnfeasible。如果nbunch原本是单个源节点现在可以用subgraph运算符实现同样效果nx.topological_sort(G.subgraph(nx.descendants(G, nbunch)))要实现逆拓扑序先把输出转为列表再反转reversed(list(nx.topological_sort(G)))十、编写同时兼容 v1.x 与 v2.x 的代码迁移指南专门用一节讨论双版本兼容Writing code that works for both versions。核心原则如下10.1 用关键字参数调用属性设置函数set_node_attributes/get_node_attributes/set_edge_attributes/get_edge_attributes的name与values顺序已变两代版本都兼容的写法是显式使用关键字 nx.set_node_attributes(G, values1.0, nameweight)10.2 去掉方法名中的_iter把任何带_iter的方法改为不带_iter的版本v1 中原版本用列表代替迭代器代码依然能跑v2 中新版本创建视图行为类似迭代器。10.3 用G.adj替换G.edgeG.edge属性已删除而G.adj在 v1 中就是G.edge两代通用。10.4 节点属性访问的双版本写法G.node.items()v1.x→G.nodes(dataTrue)v2.x 与 v1.x 均可用遍历for n in G.node:→ 直接for n in G:绝大多数G.node用法都能找到双版本通用写法唯一例外是G.node[n]v2.x 中是G.nodes[n]这在 v1.x 中不可用。好在 v2.x 中仍保留了G.node作为过渡指针因此需要兼容 v1.x 时仍可继续写G.node[n]官方计划在 v3.x 移除G.node。也就是说面向纯 v2.x 的代码用G.nodes[n]面向双版本的代码用G.node[n]。10.5 复制节点属性字典G.node[n] H.node[n]在 v1.x 中有隐患、在 v2.x 中直接报错双版本安全写法 G.nodes[n].update(H.nodes[n]) # works in v2.x10.6 被迁移方法的双版本适配被移到主命名空间的方法可通过废弃方法继续工作。若想彻底改用新函数一个双版本 hack 是按 v2.x 写法编码再为 v1.x 命名空间临时补上同名函数 if nx.__version__[0] 1: ... nx.add_path lambda G, nodes: G.add_path(nodes)而G.fresh_copy()/G.root_graph这类纯 v2.x 机制很难在 v1.x 上兼容此时最好显式确定目标图类型直接调用Graph/DiGraph/MultiGraph/MultiDiGraph构造函数。十一、Pickle 跨版本v1 与 v2 的序列化兼容Pickle 协议只存储数据、不存储类方法。因此v1 写入的 pickle 文件不能指望被 v2 直接读回图对象。如果确实遇到了这种情况官方建议的迁移路径是在装有 v1 的环境中读入 pickle把节点与边信息写出来再到装有 v2 的环境中读取这些节点/边数据添加进一个全新图 # in v1.x pickle.dump([G.nodes(dataTrue), G.edges(dataTrue)], file) # then in v2.x nodes, edges pickle.load(file) G nx.Graph() G.add_nodes_from(nodes) G.add_edges_from(edges)十二、其他值得注意的改进除基础图类外2.0 还对代码库整体做了大量改进文档明确提到的两处drawing/nx_pylab中节点居中centering的改进绘图时节点定位更规整相关代码位于 networkx/drawing/nx_pylab.py部分shortest_path例程由字典输出改为迭代器输出涉及 networkx/algorithms/shortest_paths 目录下的相关函数调用方需要注意返回类型的差异。结语从 1.X 到 2.0 的迁移本质上是一次把图数据访问方式从列表/字典复制升级为轻量、实时、支持集合运算的视图体系的 API 现代化。虽然破坏性改动较多但绝大多数失败都以清晰的异常形式呈现配合本文梳理的替换映射表G.node→G.nodes、G.edge→G.adj/G.edges、*_iter方法删除、属性设置函数参数重排、图方法迁移为命名空间函数、fresh_copy/root_graph替代__class__()即可在最短时间内完成存量代码的升级。若仍需在过渡期维护老代码请优先采用本文第十章的关键字参数与双版本惯用法。【免费下载链接】networkxNetwork Analysis in Python项目地址: https://gitcode.com/gh_mirrors/ne/networkx创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门