基本用法
序列化是将对象的状态信息转换为可以存储或传输的形式的过程。反序列化则是将这种形式的数据恢复为原始对象的过程。在Python中,执行对象序列号的标准库模块是pickle。pickle协议可以将几乎所有的Python对象转换为字节流。主要用于:
简单示例:
import pickle
# ======================
# 1. 基础对象序列化/反序列化
# ======================
data = {
"name": "test_user",
"age": 25,
"scores": [90, 88, 95],
"is_student": True
}
# dumps → 转字节流;loads → 从字节流恢复
pickle_bytes = pickle.dumps(data, protocol=pickle.HIGHEST_PROTOCOL)
restored_data = pickle.loads(pickle_bytes)
print("=== 基础对象恢复 ===")
print(restored_data)
# ======================
# 2. 写入/读取文件
# ======================
with open("data.pkl", "wb") as f:
# 写入文件,使用最高协议
pickle.dump(data, f, protocol=pickle.HIGHEST_PROTOCOL)
with open("data.pkl", "rb") as f:
loaded_from_file = pickle.load(f)
print("\n=== 从文件恢复 ===")
print(loaded_from_file)
# ======================
# 3. 自定义类实例序列化
# ======================
classStudent:
def__init__(self, name, score):
self.name = name
self.score = score
def__repr__(self):
returnf"Student(name={self.name!r}, score={self.score})"
# 创建实例
stu = Student("Tom", 92)
stu_bytes = pickle.dumps(stu)
restored_stu = pickle.loads(stu_bytes)
print("\n=== 自定义类恢复 ===")
print(restored_stu)
# ======================
# 4. 查看协议版本
# ======================
print("\n=== 协议版本 ===")
print(f"当前最高协议: {pickle.HIGHEST_PROTOCOL}")
# === 基础对象恢复 ===
# {'name': 'test_user', 'age': 25, 'scores': [90, 88, 95], 'is_student': True}
# # === 从文件恢复 ===
# {'name': 'test_user', 'age': 25, 'scores': [90, 88, 95], 'is_student': True}
# # === 自定义类恢复 ===
# Student(name='Tom', score=92)
# # === 协议版本 ===
# 当前最高协议: 4
pickle的四种方式
| |
|---|
pickle.dumps(obj) | |
pickle.loads(data) | |
pickle.dump(obj, f) | |
pickle.load(f) | |
写入文件通常会用于什么时候呢?比如我希望一个状态缓存需要多次运行间进行保持,如果用前面存入内存的话,在程序结束->内存释放->状态就会丢失
注:写入文件,存什么扩展名都可以(.pkl,.cache等)
不安全的点
序列化一个对象时,pickle 不是只存数据,而是存「一套 “重建这个对象” 的指令」。所以攻击者可以手工伪造一段pickle字节流,假如里面的指令不是“重建对象”,而是偷文件、开后门就很危险了。
import pickle
classStealFile:
def__reduce__(self):
return (print, (open("test.txt", "r", encoding="utf-8").read(),))
# 先创建一个测试文件(自动用UTF-8)
with open("test.txt", "w", encoding="utf-8") as f:
f.write("这是秘密文件内容:账号密码 123456")
# 构造恶意
picklemalicious_pkl = pickle.dumps(StealFile())
# 加载恶意 pickle → 自动读取文件!
print("开始加载恶意 pickle...")
pickle.loads(malicious_pkl)
为什么 pickle 能做到这一点?
因为 pickle 反序列化时:
- 会「递归执行对象里的
__reduce__ 方法」 __reduce__ 可以返回「任意函数 + 任意参数」
所以,pickle 只能用于:自己序列化、自己加载