Python的秘密生活:Pickle罐
Source: Dev.to
为什么会出现 “Cannot Pickle”:Python 序列化的限制
当你尝试复制或序列化某些对象——例如活跃的数据库连接、文件句柄或网络套接字——时,Python 会抛出类似 “cannot pickle …” 的 TypeError。此错误源于 pickle 模块的根本限制:它只能序列化完全位于 Python 内存中的数据。
什么是 Pickling?
Pickling 是 Python 对 序列化 的称呼:将内存中的对象转换为扁平的字节流,以便写入文件、通过网络传输或用于深拷贝。
import pickle
data = {"matches": 10, "active": True}
pickled_data = pickle.dumps(data)
print(pickled_data)
pickle.dumps()返回一个字节对象(例如b'\x80\x04\x95\x11...'),它代表原始数据。pickle.loads()则执行相反的过程,重建一个等价于原始对象的新实例。
pickle 模块记录每个对象的类型、属性以及原始数据。反序列化时,Python 按照这些记录的指令重新构建对象。
为什么有些对象无法被 Pickle
依赖操作系统管理资源的对象——例如:
- 文件描述符(打开的文件)
- 网络套接字(活跃的连接)
- 数据库连接
其状态仅在操作系统持有底层资源时才有意义。序列化原始描述符(如套接字编号)在以后将毫无用处,因为操作系统可能已将该编号重新分配给其他进程。因此,pickle 拒绝序列化此类对象并抛出 TypeError。
Pickle 与其他格式的对比
| 功能 | Pickle | JSON |
|---|---|---|
| 语言特定性 | 仅限 Python | 语言无关 |
| 支持的类型 | 几乎所有 Python 对象(包括自定义类) | 基本类型:字符串、数字、布尔值、列表、字典 |
| 人类可读 | 否(二进制) | 是(文本) |
| 安全性 | 反序列化时可执行任意代码 | 安全(不执行代码) |
如果需要与 JavaScript、Go 或其他语言编写的程序交换数据,JSON(或 MessagePack 等可互操作的格式)通常是更好的选择。
安全考虑
永远不要反序列化来自不可信来源的数据。
在反序列化过程中,Python 会执行字节流中存储的指令。恶意负载可以嵌入自动运行的代码,从而导致任意代码执行。
仅在你自己创建或已验证、可信的来源提供的数据上调用 pickle.loads()。
高级技巧:自定义序列化
对于需要特殊处理的类,实现 __reduce__(或 __reduce_ex__)方法。该方法告诉 pickle 如何序列化和重建对象。
class MyClass:
def __init__(self, value):
self.value = value
def __reduce__(self):
# 返回一个可调用对象及其参数,用于重新创建该对象
return (self.__class__, (self.value,))
小结
- Pickling = 将对象序列化为 Python 特定的字节流。
- Unpickling = 将字节流反序列化回 Python 对象。
- 边界:受操作系统管理的资源(文件、套接字、数据库连接)无法被 pickle。
- 替代方案:跨语言数据交换时使用 JSON。
- 安全规则:仅反序列化可信数据。
- 自定义:通过实现
__reduce__获得对序列化过程的细粒度控制。