Python 3.6加载Pickle文件报错"__builtin__"模块未找到的解决方案
在使用Python 3.6加载由Python 2生成的Pickle文件时,经常会遇到ModuleNotFoundError: No module named '__builtin__'的错误。这个问题根源在于Python 2和Python 3的模块名称发生了变化,本文会详细解释原因并给出多种可行的解决思路。
问题产生的原因
Python 2中有一个内置模块叫做__builtin__,它包含了所有Python的内置函数和异常类型。但在Python 3中,这个模块被重命名为builtins(注意末尾多了一个s)。
当Python 2序列化(pickle)一个包含自定义类或者复杂对象的文件时,Pickle会在文件中记录对象的模块路径,比如__builtin__.SomeClass。当你用Python 3去反序列化(load)时,解释器会尝试导入__builtin__模块,发现Python 3中根本没有这个模块,于是就抛出了上述错误。
解决方案一:添加模块映射(推荐)
最常用且侵入性最小的解决方式是在加载Pickle文件之前,手动给sys.modules添加映射关系,让Python 3把对__builtin__的引用指向builtins。
import pickle
import sys
# 核心修复代码:将Python 2的__builtin__映射到Python 3的builtins
sys.modules['__builtin__'] = __import__('builtins')
try:
with open('data.pkl', 'rb') as f:
data = pickle.load(f)
print("文件加载成功")
print(data)
except Exception as e:
print(f"加载失败: {e}")这段代码的原理是修改Python的模块缓存表,当Pickle反序列化过程中尝试查找__builtin__模块时,系统会直接返回builtins模块的内容,从而绕过模块不存在的错误。
解决方案二:自定义Unpickler
如果你不想修改全局的sys.modules,或者遇到了更复杂的映射问题,可以继承pickle.Unpickler类,重写find_class方法,在类查找阶段就做兼容处理。
import pickle
import sys
class CompatibleUnpickler(pickle.Unpickler):
def find_class(self, module, name):
# 处理Python 2到Python 3的模块名映射
if module == '__builtin__':
module = 'builtins'
# 也可以在这里添加其他自定义的映射规则
# if module == 'cPickle':
# module = 'pickle'
return super().find_class(module, name)
try:
with open('data.pkl', 'rb') as f:
# 使用自定义的Unpickler加载
data = CompatibleUnpickler(f).load()
print("文件加载成功")
except Exception as e:
print(f"加载失败: {e}")这种方式更加灵活,你可以在这个方法里添加任何你需要的模块名替换逻辑,特别适合处理那些由第三方库生成、包含特殊模块路径的Pickle文件。
解决方案三:重新生成Pickle文件(根本解决)
如果条件允许,最好的办法还是在Python 3环境下重新生成Pickle文件。Pickle格式本身并不是完全跨版本兼容的,尤其是涉及到自定义类的时候。
如果数据源是Python 2代码生成的,建议编写脚本将Python 2的数据导出为JSON或者CSV等通用格式,再在Python 3中读取并重新序列化。
# Python 2 环境执行(导出为JSON)
import json
import pickle
with open('data.pkl', 'rb') as f:
data = pickle.load(f)
with open('data.json', 'w') as f:
json.dump(data, f)
# Python 3 环境执行(重新生成pickle)
import json
import pickle
with open('data.json', 'r') as f:
data = json.load(f)
with open('data_py3.pkl', 'wb') as f:
pickle.dump(data, f)注意事项与局限性
- Pickle文件本质上包含的是Python对象的字节流,它严重依赖具体的Python版本和类定义。如果Pickle文件中包含了Python 2特有的类(比如某些在Python 3中被移除或重构的类),即使修复了
__builtin__的问题,也可能遇到其他属性错误。 - 不要随意加载来源不明的Pickle文件,因为Pickle反序列化过程可以执行任意代码,存在安全风险。
- 如果Pickle文件非常大,或者是在生产环境中使用,建议优先采用方案二(自定义Unpickler),因为它对全局环境的影响最小。
总结
遇到ModuleNotFoundError: No module named '__builtin__'报错时,核心思路就是让Python 3能够识别原本属于Python 2的模块路径。通过sys.modules映射是最快的修复手段,通过自定义Unpickler是更规范的工程化手段,而重新生成数据文件则是从根源上杜绝兼容性问题的最佳实践。