Python中的序列化(Pickling)和反序列化(Unpickling)
在 Python 中,序列化(Pickling)和反序列化(Unpickling)是将对象转换为字节流以便存储或传输,以及将字节流还原为对象的过程。Python 提供了一个标准模块 pickle 来专门处理这项工作。
以下是详细的指南:
1. 核心概念
序列化 (Pickling):
- 将 Python 对象层次结构转换为字节流(byte stream)。
- 用途: 将对象保存到文件、存入数据库、或通过网络传输。
- 对应函数:
dump()或dumps()。
反序列化 (Unpickling):
- 将字节流转换回 Python 对象层次结构。
- 用途: 读取保存的数据,恢复对象的状态。
- 对应函数:
load()或loads()。
2. 基本用法
首先需要导入模块:
python
import pickle
A. 字符串/字节流操作 (dumps 和 loads)
当你不需要写入文件,只想把对象变成字节对象(bytes)时使用。
python
import pickle
# 定义一个复杂的对象(字典包含列表)
data = {
'name': 'Alice',
'age': 30,
'scores': [85, 90, 88],
'is_active': True
}
# 1. 序列化 (Pickling) -> 变为 bytes
binary_data = pickle.dumps(data)
print(f"序列化后的数据类型: {type(binary_data)}")
print(f"序列化后的内容 (部分): {binary_data[:20]}...")
# 2. 反序列化 (Unpickling) -> 变回 对象
restored_data = pickle.loads(binary_data)
print(f"还原后的数据: {restored_data}")
print(f"是否相等: {data == restored_data}")
B. 文件操作 (dump 和 load)
直接将对象写入文件或从文件读取。注意:必须使用二进制模式 ('wb' 和 'rb')。
python
import pickle
data = ['apple', 'banana', 'cherry']
# 1. 序列化到文件
with open('fruit.pkl', 'wb') as f:
pickle.dump(data, f)
print("数据已写入 fruit.pkl")
# 2. 从文件反序列化
with open('fruit.pkl', 'rb') as f:
loaded_data = pickle.load(f)
print(f"从文件读取的数据: {loaded_data}")
3. 可以被 Pickle 的类型
并不是所有 Python 对象都能被序列化。支持的类型包括:
None,True,False- 整数、浮点数、复数
- 字符串、字节 (bytes)、字节数组 (bytearrays)
- 只包含可 pickle 对象的元组、列表、集合、字典
- 在模块顶层定义的 函数(通过引用序列化,不存代码逻辑)
- 在模块顶层定义的 类
- 类的实例(如果该类的
__dict__或__getstate__()是可序列化的)
通常不能被 Pickle 的对象:
- 打开的文件句柄(File objects)
- 网络连接(Sockets)
- 数据库连接
- 生成器(Generators)
- Lambda 函数(因为它们是匿名的,反序列化时很难找到原定义)
4. ⚠️ 极其重要的安全警告
千万不要对不信任的数据进行反序列化(Unpickling)!
pickle 模块不是安全的。构建恶意的 pickle 数据可以在反序列化时执行任意代码。
黑客攻击示例(原理演示):
python
import pickle
import os
class Malicious:
def __reduce__(self):
# 当对象被反序列化时,会执行这个命令
# 这里演示打印一句话,实际上可以是删除文件或窃取数据
return (os.system, ("echo '你被攻击了!'",))
# 攻击者生成恶意数据
malicious_data = pickle.dumps(Malicious())
# 受害者加载数据
pickle.loads(malicious_data) # 输出: 你被攻击了! (并返回执行结果)
结论: 如果你需要处理来自用户上传或网络的不受信任数据,请使用 JSON。
5. Pickle vs JSON
| 特性 | Pickle | JSON |
|---|---|---|
| 通用性 | Python 专用 (其他语言无法读取) | 通用 (几乎所有语言支持) |
| 数据格式 | 二进制 (Binary) | 文本 (Text/String) |
| 可读性 | 人类不可读 | 人类可读 |
| 支持类型 | 支持大多数 Python 类型 (包括自定义类) | 仅支持基本类型 (dict, list, str, num, bool) |
| 安全性 | 不安全 (可执行代码) | 安全 |
| 速度 | 通常较快 | 较慢 (需要解析文本) |
选择建议:
- 如果是 Python 程序内部短期存储状态,且完全信任数据源 Pickle。
- 如果是跨语言交互、存储配置文件、或处理外部数据 JSON。
6. 进阶:自定义序列化行为
如果一个对象包含不能被序列化的属性(如打开的文件),你可以通过魔术方法 __getstate__ 和 __setstate__ 来控制序列化过程。
python
import pickle
class TextReader:
def __init__(self, filename):
self.filename = filename
self.file = open(filename, 'r') # 文件句柄不能被 pickle
self.content = self.file.read()
# 序列化时调用:决定存什么
def __getstate__(self):
state = self.__dict__.copy()
# 移除不可序列化的文件句柄
del state['file']
return state
# 反序列化时调用:决定如何恢复
def __setstate__(self, state):
self.__dict__.update(state)
# 重新打开文件
self.file = open(self.filename, 'r')
# 使用示例(假设 test.txt 存在)
# obj = TextReader('test.txt')
# serialized = pickle.dumps(obj)
# restored = pickle.loads(serialized)
总结
- Pickle 是 Python 原生的序列化工具,功能强大但只能在 Python 间使用。
- 使用
dump/load处理文件,dumps/loads处理内存字节。 - 安全第一:永远不要 Unpickle 来源不明的数据。