[python] 从pickle.load() ModuleNotFoundError说起
一、问题描述#
当我用pickle.load()
在python项目B中读取在另一个项目A中通过pickle序列化存储的自定义的类对象文件时,出现了如下报错:
ModuleNotFoundError: No module named 'xxx'
将项目A中的自定义类文件复制到项目B后仍旧出现该报错。
二、问题分析#
该问题是由pickle封存类实例的方式导致的。
python的pickle模块支持将自定义类对象序列化和反序列化。
通俗地讲,我们可以利用pickle将自己定义的python类型实例转化为字节流,将字节流保存到文件中(序列化)。
当我们想要使用这些数据时,pickle模块也提供了对应的读取方法将文件中的字节流还原回原来的python对象(反序列化)。
在进行类实例的封存时,类体和类数据不会跟着实例一起被封存到文件中,只有类的数据会被封存。
def save(obj):
return (obj.__class__, obj.__dict__)
这样设计可以使得后续类在修复错误、新增方法时,仍然可以载入原来版本类实例封存的数据。
注意,在保存的.pkl文件中,类的信息是通过类的限定名称(qualified name)记录的,限定名称是一个以点号分隔的名称,显示从模块的全局作用域该模块中定义的某个类、函数或方法的“路径”,例如:email.mime.text
。
在读取.pkl文件还原python对象时,我们需要两部分信息:类的定义以及对象的数据。对于类的定义,pickle.load方法通过类的限定名称找到定义类的具体代码得到信息,而对象数据则直接从.pkl文件当中读取。
在实例解封时,pickle会默认创建一个未初始化的类实例,然后还原其属性。
def restore(cls, attributes):
obj = cls.__new__(cls)
obj.__dict__.update(attributes)
return obj
当pickle.load方法根据文件中记录的类限定名称无法找到类的定义时,就会抛出ModuleNotFoundError。
所以,当出现该错误时,需要保证:
- 类定义代码是存在的
- 在调用pickle.load的文件中能通过类限定名访问到类定义
对于引用的第三方库,一般类限定名称会以 库名.模块名 的形式存在,出现该问题更可能是因为库的版本不同,模块整体被移除导致的,需要判断当前环境中的库版本与序列化类对象的原始代码中库的版本是否一致。
三、解决方法#
在问题分析中已经讨论了该报错的原因,在我的情境中,是由于两个不同项目下自定义类的限定名称不同导致pickle.load无法找到类定义导致了报错。
因此,需要保证在项目B中调用pickle.load的文件中可以通过与项目A中调用pickle.save函数文件一致的路径访问到相关类定义。
可以通过在项目B中,将类定义代码存储到与项目A内部同样的路径下,并通过sys模块调整起始搜索路径。
import sys
sys.path.append(<path>)
四、参考资料#
1.python官方文档-pickle——Python对象序列化
2.桃子小迷妹.(2.10,2022). 《Python pickle读取文件 ModuleNotFoundError: No module named》
3.仙远.(3.26,2022). 《pickle.load报错 ModuleNotFoundError: No module named ‘sklearn.svm.classes‘》
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」