Python 对象序列化有明确边界:json 仅支持基本类型且安全通用,pickle 功能强但限于 Python 环境且有安全风险;含文件句柄、线程锁等对象不可直接序列化;应依场景选方案,避免越界误用。

Python 对象序列化不是万能的,它有明确的适用边界和典型场景。用对地方能大幅提升开发效率,用错地方反而引发安全、性能或兼容性问题。
哪些对象可以被安全序列化
Python 内置类型(如字典、列表、字符串、数字、None)和简单自定义类实例(不含可调用属性、文件句柄、线程锁等)通常可被 pickle 或json处理。
- json只支持:dict、list、str、int、float、bool、None —— 且 key 必须是字符串
- pickle支持更多(如函数、类、带状态的对象),但仅限 Python 环境间交换,且反序列化存在执行任意代码风险
- 含
lambda、open()返回的文件对象、threading.Lock、数据库连接等,基本无法直接序列化
典型可用场景与对应方案
不同场景需匹配不同序列化方式,不能一概而用:
- 跨进程 / 网络传递简单数据:用
json(安全、通用、人可读),比如 Flask API 返回值、HTTP 请求体 - Python 内部缓存或临时持久化:用
pickle(保留类型和方法),比如 joblib 缓存训练好的 sklearn 模型 - 配置或用户偏好存储 :用
json或toml(结构清晰、易编辑),避免用 pickle——防止 配置文件 被恶意篡改触发代码执行 - 高并发服务间通信 :考虑
msgpack或protobuf(体积小、解析快),比 json 更高效,比 pickle 更安全
常见越界误用及后果
忽视序列化边界容易导致运行时错误或隐患:
- 试图用
json.dumps()序列化datetime对象 → 报TypeError;需预处理(如转为 ISO 字符串)或用default参数定制 - 用
pickle保存带闭包或局部函数的对象 → 反序列化失败,因依赖的 命名空间 不可见 - 将含数据库游标或 socket 连接的对象塞进 Redis 缓存 → 序列化可能“成功”,但还原后对象已失效,后续调用直接崩溃
- 在不受信输入上使用
pickle.load()→ 攻击者可构造恶意 字节 流,执行任意系统命令
替代思路:不序列化,也能传状态
当对象本身难以序列化时,优先考虑“传描述,不传实体”:
- 不缓存整个 DataFrame,而缓存其路径 + 读取参数(如
{"path": "data.parquet", "columns": ["a","b"]}) - 不序列化类实例,而记录初始化所需的关键参数(如
{"class": "MyModel", "params": {"lr": 0.01}}),用工厂函数重建 - 对复杂对象提供
__getstate__/__setstate__方法,显式控制哪些字段参与 pickle,排除不可序列化部分
序列化是 工具,不是目的。判断一个对象要不要、能不能、该用哪种方式序列化,关键看它要流向哪里、由谁还原、是否可信、是否长期存储。清楚边界,才能用得稳、用得准。