networkx图序列化应避免pickle,推荐用node_link_data转字典后JSON存储,需预处理numpy类型和float('inf'),加载时手动恢复属性类型。

用 networkx 序列化图结构时,别直接存原始图对象
Python 中没有“网络流序列化”这种标准概念——图结构本身不是流,但你可以把图(比如 networkx.DiGraph)转成适合网络传输或磁盘存储的通用格式。直接用 pickle 保存图对象最省事,但有严重隐患:pickle 不跨语言、不向前兼容、反序列化可能执行任意代码。生产环境必须避免。
推荐路径是:先转成中间结构(如邻接表、边列表或 JSON 可表达的字典),再用安全格式持久化。
-
networkx.write_adjlist()生成纯文本邻接表,人可读,但丢失节点属性和权重类型(全转成字符串) -
networkx.node_link_data()返回嵌套字典,保留节点/边所有属性,能直接json.dump(),但注意:JSON 不支持float('inf')、complex、自定义对象——遇到就报TypeError: Object of type float is not JSON serializable - 若图含 NumPy 类型(如
np.int64),需预处理:用json.dumps(obj, default=lambda x: x.item() if hasattr(x, 'item') else str(x))
保存带权重的有向图到 JSON 文件时,node_link_data 是首选但要清理数据类型
这是最常见需求:图有节点坐标、边容量、整数 ID,想存成文件供其他服务读取。直接调 networkx.node_link_data(G) 得到的字典看着干净,但实际暗藏坑:
- 节点 ID 若是
numpy.int64,JSON 会拒绝序列化;解决方法:遍历data['nodes'],对每个'id'字段调int() - 边的
'capacity'若为float('inf'),得提前替换成None或一个极大整数(如10**18),并在加载时还原 - 示例片段:
import networkx as nx
import json
<p>G = nx.DiGraph()
G.add_edge('s', 'a', capacity=10)
G.add_edge('a', 't', capacity=float('inf'))</p><p>data = nx.node_link_data(G)</p><div class="aritcle_card flexRow">
<div class="artcardd flexRow">
<a class="aritcle_card_img" href="/xiazai/skill9754" title="Secure Python Code Sandbox"><img
src="https://img.php.cn/upload/skill/001/246/273/6adaf20d3191a227.jpg" alt="Secure Python Code Sandbox" ></a>
<div class="aritcle_card_info flexColumn">
<a href="/xiazai/skill9754" title="Secure Python Code Sandbox">Secure Python Code Sandbox</a>
<p>安全 Python 代码沙箱:在隔离的沙箱中执行任意 Python 代码,预装 requests、NumPy、pandas 库。返回 stdout、stderr。</p>
</div>
<a href="/xiazai/skill9754" title="Secure Python Code Sandbox" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a>
</div>
</div><p><span>立即学习</span>“<a href="https://pan.quark.cn/s/00968c3c2c15" style="text-decoration: underline !important; color: blue; font-weight: bolder;" rel="nofollow" target="_blank">Python免费学习笔记(深入)</a>”;</p><h1>清理 inf</h1><p>for edge in data['links']:
if edge.get('capacity') == float('inf'):
edge['capacity'] = None</p><p>with open('flow_graph.json', 'w') as f:
json.dump(data, f)</p>从 JSON 加载后重建 networkx 图,记得手动恢复边属性类型
JSON 只存字符串、数字、布尔、null,所有类型信息都丢了。比如你存了 "capacity": 10,加载回来还是 int,没问题;但存了 "capacity": null,就得自己判断是否该设为 float('inf')。
- 用
nx.node_link_graph(data)能快速建图,但所有边属性都是原样字符串/数字,不会自动转回业务需要的类型 - 如果边上有
'cost'(浮点)、'is_critical'(布尔),必须遍历G.edges(data=True)手动转换 - 更稳妥的做法:封装一个
load_flow_graph(filename)函数,在里面做类型映射,比如if 'capacity' in attrs: attrs['capacity'] = float('inf') if attrs['capacity'] is None else attrs['capacity']
真要走“网络流”语义,序列化前先确认你存的是什么
这个词容易误导:如果你实际要做最大流计算(如用 networkx.maximum_flow()),那图结构 + 容量属性就够了;但如果你存的是某次运行后的流值分配(即每条边上的当前流量),那就得额外存 flow_dict 结构——它不是图的一部分,而是计算结果。
- 不要把流值硬塞进边的
'capacity'属性里,否则下次跑算法会出错 - 推荐分开存:一个
graph.json(拓扑+容量),一个flow_solution.json(形如{"s": {"a": 5.0}, "a": {"t": 5.0}}) - 如果用
ortools或cvxpy解流问题,它们通常输出 numpy 数组,此时必须用.tolist()再存 JSON,否则报错
类型擦除是序列化的根本代价,没有银弹。每次存之前,花三十秒想清楚:这个字段将来是当数字算,还是当标签查,还是只做展示——决定了你清理和恢复的粒度。

















