pickle最直接但不安全且不跨语言;json安全跨语言但仅支持基础类型;复杂对象优先专用格式(如Parquet、PTH);自定义类用__getstate__/__setstate__精准控制序列化内容。

用 pickle 保存任意 Python 对象最直接,但有安全和跨语言限制
如果对象只在 Python 内部读写(比如缓存中间计算结果、保存训练好的模型),pickle 是默认选择。它能处理函数、类实例、嵌套字典、自定义对象等绝大多数结构。
常见错误是直接用 open(..., 'w') 写入 —— pickle 必须用二进制模式:open(..., 'wb') 写,open(..., 'rb') 读。
示例:
import pickle
<p>data = {'a': [1, 2, 3], 'b': lambda x: x * 2} # 含 lambda,json 不支持
with open('data.pkl', 'wb') as f:
pickle.dump(data, f) # 注意是 dump,不是 dumps</p><p>with open('data.pkl', 'rb') as f:
loaded = pickle.load(f) # 注意是 load,不是 loads
</p>
-
pickle协议版本影响兼容性:Python 3.8+ 默认用 protocol 5,老版本可能打不开;显式指定protocol=pickle.HIGHEST_PROTOCOL或protocol=4更稳妥 - 反序列化时会执行任意代码(如
__reduce__返回的可调用),切勿加载不可信的.pkl文件 - 无法被其他语言解析,Java/JS 拿不到这个文件的内容
用 json 存结构化数据更安全,但要求对象“可 JSON 序列化”
如果目标是配置、日志、API 数据交换或需要跨语言读取,json 是事实标准。但它只支持基础类型:dict、list、str、int、float、bool、None。
遇到 datetime、set、NumPy 数组、自定义类实例时会报 TypeError: Object of type X is not JSON serializable。
解决办法是传 default 参数做类型转换:
import json
from datetime import datetime
<p>data = {'time': datetime.now(), 'tags': {'python', 'io'}}</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><p>def json_fallback(obj):
if isinstance(obj, datetime):
return obj.isoformat()
elif isinstance(obj, set):
return list(obj)
raise TypeError(f"Object of type {type(obj).<strong>name</strong>} is not JSON serializable")</p><p>with open('data.json', 'w') as f:
json.dump(data, f, default=json_fallback, indent=2)
</p>
- 别用
str(obj)当 fallback —— 它可能返回<__main__.myclass object at></__main__.myclass>,丢失信息 -
json.dumps()输出字符串,json.dump()直接写文件,别混用 - 中文字符默认被转义(
\u4f60),加ensure_ascii=False保持可读性
复杂对象(如 Pandas DataFrame、PyTorch 模型)优先用专用格式
通用序列化工具往往效率低、体积大、恢复慢。特定生态已有优化方案:
- Pandas
DataFrame:用df.to_parquet('data.parquet')(快、压缩好、支持列过滤),比to_pickle更适合长期存储 - PyTorch 模型:用
torch.save(model.state_dict(), 'model.pth'),而非pickle.dump(model, ...)—— 前者只存参数,轻量且不绑定类定义 - 大型嵌套结构 + 需要查询字段:考虑
msgpack(比 JSON 快、体积小,仍为二进制,但无代码执行风险)
这些方案的共同点是:不试图“通用”,而是针对数据形态和使用场景做了取舍 —— 你存的时候知道怎么用,就该选它。
自定义类序列化时,__getstate__ 和 __setstate__ 比 __dict__ 更可控
直接让 pickle 序列化实例,它默认用 obj.__dict__。但有些字段不该存(如临时缓存、数据库连接、线程锁),有些字段需特殊处理(如可变默认值)。
这时重写 __getstate__ 过滤/转换状态,__setstate__ 控制重建逻辑:
class CacheManager:
def __init__(self):
self.data = {}
self._cache = {} # 不该序列化
self._lock = threading.Lock() # 不能被 pickle
<pre class="brush:php;toolbar:false;">def __getstate__(self):
state = self.__dict__.copy()
# 删除不可序列化的项
state.pop('_cache', None)
state.pop('_lock', None)
return state
def __setstate__(self, state):
self.__dict__.update(state)
# 重建运行时所需字段
self._cache = {}
self._lock = threading.Lock()
- 别在
__setstate__里调用self.__init__()—— 可能触发重复初始化逻辑 - 如果类依赖外部模块(如 C 扩展),确保反序列化环境已导入对应模块,否则报
ModuleNotFoundError
真正麻烦的从来不是“怎么存”,而是“哪些不该存”和“下次加载时怎么让它还像原来那样工作”。这两点想清楚了,选什么序列化方式反而次要。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










