pickle能序列化几乎所有python对象(如函数、类实例、numpy数组),而json仅支持基础类型;但pickle不跨语言、不安全、版本兼容性差,仅适用于可信环境的同版本python间传递。

为什么用 pickle 而不是 json?
因为 pickle 能序列化几乎所有 Python 原生对象(函数、类实例、datetime、嵌套自定义对象等),而 json 只支持基础类型(dict、list、str、int、float、bool、None)。如果你存的是 numpy.ndarray、sklearn.model 或带方法的类实例,json 直接报 TypeError: Object of type ... is not JSON serializable。
但代价是:pickle 文件不跨语言、不安全(反序列化恶意数据会执行任意代码)、版本兼容性差(Python 3.8 dump 的文件在 3.12 可能 load 失败)。
- 只在可信环境用,别加载不可信的
.pkl文件 - 同一 Python 版本间传递最稳;跨版本优先用
protocol=4(Python 3.8+ 默认)或显式指定protocol=5(3.8+ 支持) - 想跨语言或长期归档,改用
joblib(适合 numpy/scikit-learn)或转成hdf5/parquet
pickle.dump() 和 pickle.load() 怎么写才不报错?
常见错误是忘记以二进制模式打开文件:open(..., 'w') 会触发 TypeError: write() argument must be str, not bytes;open(..., 'r') 则报 UnicodeDecodeError。
必须用 'wb' 和 'rb':
import pickle
<p>data = {'a': [1, 2, 3], 'b': lambda x: x*2} # 含函数,json 不行</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill4769" title="Python Testing"><img
src="https://img.php.cn/upload/skill/000/000/081/179021887894914.jpg" alt="Python Testing" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/skill4769" title="Python Testing" class="overflowclass">Python Testing</a>
<p class="overflowclass">Python 测试速查:运行 pytest、使用 mock/patch、参数化、fixtures、异步、覆盖率测试。</p>
</div>
<a rel="nofollow" href="/xiazai/skill4769" title="Python Testing" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div><h1>✅ 正确:二进制写</h1><p>with open('data.pkl', 'wb') as f:
pickle.dump(data, f)</p><h1>✅ 正确:二进制读</h1><p>with open('data.pkl', 'rb') as f:
loaded = pickle.load(f)</p>
- 不要用
pickle.dumps()/pickle.loads()直接处理大对象——它们把整个对象塞进内存,容易 OOM - 多个对象连续
dump到同一文件,得用多次load读取(它不会自动分隔),否则第二次load报EOFError - 路径不存在时,
dump不会自动建目录,先用os.makedirs(os.path.dirname(path), exist_ok=True)
保存大型对象(如模型)时性能很差,怎么优化?
默认 pickle 协议(Python 3.8+ 是 protocol 4)对大 dict/list 效率一般。两个实际有效的提速点:
- 显式指定更高协议:
pickle.dump(obj, f, protocol=5)(Python 3.8+ 支持,比 protocol 4 快 10–20%,尤其含大量字符串时) - 用
joblib.dump()替代(尤其对 numpy 数组、scikit-learn 模型):内部做了内存映射和分块,比原生pickle快 2–5 倍,且压缩可选
# joblib 示例(需 pip install joblib)
from joblib import dump, load
import numpy as np
<p>arr = np.random.rand(10000, 1000)
dump(arr, 'array.joblib') # 自动压缩,更快
loaded_arr = load('array.joblib')</p>
注意:joblib 不是万能替代——它对纯 Python 对象(比如没用 numpy 的 class 实例)可能反而更慢,且不支持所有 pickle 能存的类型(如某些 C 扩展对象)。
反序列化时遇到 ModuleNotFoundError 或 AttributeError 怎么办?
这是因为 pickle 存的是“类名+模块路径”,不是代码本身。如果 load 时模块不在 sys.path,或类定义被重命名/移动了,就会崩。
典型错误信息:ModuleNotFoundError: No module named 'mymodule' 或 AttributeError: Can't get attribute 'MyClass' on <module from></module>
- 确保
load时运行环境的包结构、导入路径和dump时完全一致(包括是否在__main__中定义类) - 避免在脚本顶层定义要持久化的类;改为放在独立
.py文件里,用import加载 - 紧急修复法:在
load前手动补模块别名,例如sys.modules['old_module'] = new_module(不推荐长期用)
最稳妥的做法,是在项目中固定模块入口(比如统一用 from myproject.models import MyModel),并把 pickle 文件和代码版本一起管理。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










