对netwrokx对象pickle序列化踩过的坑

2023-10-13 16:47:20 浏览数 (2)

今天聊聊我这几天在开发过程中遇到的一些序列化和反序列化踩过的坑。


1.序列化和反序列化

首先他们是相对的概念,序列化就是在数据处理时,将数据转化成可存储的和可传输的格式,反序列化就是将数据还原成原来的数据格式。

这就支持将自定义的对象和任意的对象统统存储在文件或者数据库,因为当我们将一个类实例化成一个对象后,这个对象的数据一般就是在内存中动态存储的。程序退出执行后,对象随即被销毁,下次再运行程序时,对象动态生成。虽然从输出结果来看,这一次运行的对象的数据,和上一次对象的数据一样,但其实这里的两个对象是完全没有关系的,python建立了映射到内存对象的地址,如果去打印这个地址,会发现两次的存储空间截然不同。

第一次运行时的地址第一次运行时的地址

注:id()方法是用来获取对象的内存地址

第二次运行时的地址第二次运行时的地址

因此我们将这个对象数据序列化后,我们下次使用时就可以再次反序列化,直接从文件中或者数据库中读取。

当一个图对象包含几万个节点,几千万条边的时候,每次都使用业务逻辑动态生成就会带来很大的时间成本花销,所以这个时候序列化和反序列化就很有必要了。但请注意,这只适合数据不常发生更改的情况,如果你的数据需要反复的改动,那么支持动态添加删除节点和边的图数据库(如neo4j)更加适合。

2.标准库pickle

pickle支持序列化和反系列化各种python对象,它的用法也非常简单,使用dump()函数来序列化文件,使用load()函数来反序列化文件。以下是一个用法模版:

代码语言:python代码运行次数:0复制
import pickle
with open('demo.pkl','wb') as file:
    pickle.dump(demo,file)

这时候就会生成一个demo.pkl文件,再通过load()函数就可以加载出来。

代码语言:python代码运行次数:0复制
with open('demo.pkl','rb') as f:
    demo_load = pickle.load(f)

python的pickle在序列化是和反序列化时不需要遵守很多的规定,但也因此存在很多的限制。例如:由于是python的标准库,所以不能跨源,使用pickle进行序列化,只能再使用pickle。而且比起其他的序列化与反序列化工具使用pickle的效率并不高。

3.坑1:序列化的路径

由于在编写图网络对象的业务逻辑是在本地编写的,在本地已经序列化了一份数据,所以迁移到后端时,反序列化就解析不出来对象信息。

这是因为在序列化时,python会将对象所有的用到的依赖,也就是自己写的包和第三方库扫描一遍,把它们的导入路径在写在序列化的数据文件中,如我项目名称为demo,在这个项目里面我封装了一个工具tools.py,这时它的绝对导入路径就是"/demo/tools",但是我在迁移时它的路径就改为了后端项目下的路径:"/backend/demo/tools",如果此时反序列化,python检测不到路径"/demo/tools",自然会报错,此时的解决办法就是删除已经序列化的文件,重新在当前项目下序列化一份数据即可。

4.坑2:序列化的函数位置

将数据重新在后端环境下序列化一次后,我开始了接口query_graph的编写,直接在接口里面写了个load函数,然后运行,这其中呢,这个时候就又出错了,还是无法解析依赖内容,请教了大佬后,捯饬了两个小时,弄清楚了,这是因为我的序列化函数dump是在业务逻辑work.py中编写的,所以必须在work.py里面写load函数,也就是说,不能直接运行query_graph.load,要使用query_graph.data = work.load。说白了,还是序列化和反序列化的路径必须保持一致的问题。

5.坑3:不看官方文档

我选择使用pickle是因为序列化和反序列化,但是networkx官方提供了丰富的读写函数,其中就包括了序列化但序列化:

当我改完bug,喘一口气却在浏览官方文档时看到这个内容时,我的心情be like:

大无语!!!大无语!!!

所以各位看官老爷一定要养成多看文档的好习惯!!!

附nx官方参考文档:https://networkx.org/documentation/stable/reference/readwrite/index.html

我正在参与2023腾讯技术创作特训营第二期有奖征文,瓜分万元奖池和键盘手表

0 人点赞