前言
大家好,我是 倔强青铜三 。欢迎关注我,微信公众号: 倔强青铜三。点赞、收藏、关注,一键三连!
欢迎来到 苦练Python第 73 天!
今天我们来学习python标准库中的pickle库。
在Python开发中,我们经常需要将内存中的对象(如列表、字典或自定义类的实例)保存到磁盘,或在网络传输中传递对象状态。
这时候,pickle模块就成为了关键工具。
作为Python标准库中专门用于对象序列化与反序列化的模块,它能将复杂的Python对象转换为二进制字节流(序列化),也能将字节流还原为原始对象(反序列化)。
本文将从基础概念出发,逐步拆解pickle的核心用法,并总结常见陷阱与注意事项。
一、模块简介与导入
pickle是Python内置的标准库,无需额外安装。
其名称灵感来源于"腌渍"(像腌渍食物一样保存对象状态),官方文档中称其为"Python对象序列化协议"。
要使用pickle,只需在代码开头导入模块:
python
import pickle
运行结果:无报错(仅完成模块导入)。
二、核心概念
序列化(Serialization)
将内存中的Python对象(如列表、类实例等)转换为二进制字节流的过程。
序列化后的数据可存储在文件、数据库中,或通过网络传输。
反序列化(Deserialization)
将二进制字节流重新转换为原始Python对象的过程。
反序列化后,对象的状态(如属性值、方法关联)与序列化前完全一致。
字节流(Byte Stream)
序列化的结果是二进制形式的字节序列(bytes类型),而非人类可读的文本(如JSON的字符串)。
这使得pickle能高效处理复杂对象,但也牺牲了跨语言兼容性(仅Python可直接解析)。
三、常用函数
pickle提供了4个核心函数,分别用于序列化和反序列化的不同场景:
| 函数 | 功能 |
|---|---|
pickle.dump(obj, file) |
将对象obj序列化为字节流,写入文件对象file(需以二进制模式打开) |
pickle.dumps(obj) |
将对象obj序列化为字节流(返回bytes类型) |
pickle.load(file) |
从文件对象file(需以二进制模式打开)中读取字节流,反序列化为对象 |
pickle.loads(bytes) |
将字节流bytes反序列化为对象 |
四、文件操作模式
由于pickle处理的是二进制字节流,文件必须以二进制模式打开:
- 写入时使用
'wb'(覆盖写)或'ab'(追加写)模式; - 读取时使用
'rb'(只读)或'ab+'(读写)模式。
若错误使用文本模式(如'w'或'r'),会抛出TypeError: a bytes-like object is required, not 'str'。
五、协议版本
pickle支持多种序列化协议(通过protocol参数指定),不同协议的字节流格式和兼容性不同:
- 协议0:ASCII格式,兼容所有Python版本,但效率低;
- 协议1:二进制格式,兼容Python 1.0+;
- 协议2:优化后的二进制格式(Python 2.3+);
- 协议3:Python 3专用的二进制格式(不支持Python 2);
- 协议4:Python 3.4+引入,支持更大对象(如超过4GB的数组);
- 协议5:Python 3.8+引入,支持带外数据(out-of-band data)。
默认协议 :Python 3.8及以上默认使用协议4;Python 3.7及以下默认使用协议3。实际开发中,若无需兼容旧版本,建议使用默认协议(无需显式指定protocol参数)。
六、常见可序列化对象
pickle能序列化大多数Python内置类型和自定义对象,以下是典型示例:
1. 列表
python
import pickle
# 原始列表
original_list = [1, "hello", [2, 3], {"key": "value"}]
# 序列化为字节流(dumps)
bytes_data = pickle.dumps(original_list)
print("序列化后的字节流:", bytes_data)
# 运行结果:序列化后的字节流: b'\x80\x04\x95\x1d\x00\x00\x00\x00\x00\x00\x00\x8c\x01\x94\x8c\x05hello\x94\x8c\x02\x94\x8c\x02\x94\x8c\x03\x94\x93\x94\x8c\x03key\x94\x8c\x05value\x94\x86\x94.' (实际输出可能因环境略有差异)
# 反序列化(loads)
restored_list = pickle.loads(bytes_data)
print("反序列化后的列表:", restored_list)
# 运行结果:反序列化后的列表: [1, 'hello', [2, 3], {'key': 'value'}]
2. 字典
python
import pickle
original_dict = {"name": "Alice", "age": 30, "hobbies": ("reading", "coding")}
# 写入文件(dump)
with open("dict.pkl", "wb") as f:
pickle.dump(original_dict, f)
# 从文件读取(load)
with open("dict.pkl", "rb") as f:
restored_dict = pickle.load(f)
print("原始字典:", original_dict)
# 运行结果:原始字典: {'name': 'Alice', 'age': 30, 'hobbies': ('reading', 'coding')}
print("恢复字典:", restored_dict)
# 运行结果:恢复字典: {'name': 'Alice', 'age': 30, 'hobbies': ('reading', 'coding')}
3. 自定义类实例
python
import pickle
# 定义自定义类
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def greet(self):
return f"Hello, I'm {self.name}, {self.age} years old."
# 创建实例
p = Person("Bob", 25)
# 序列化到文件
with open("person.pkl", "wb") as f:
pickle.dump(p, f)
# 反序列化并验证
with open("person.pkl", "rb") as f:
p_restored = pickle.load(f)
print("原始对象属性:name={}, age={}".format(p.name, p.age))
# 运行结果:原始对象属性:name=Bob, age=25
print("恢复对象属性:name={}, age={}".format(p_restored.name, p_restored.age))
# 运行结果:恢复对象属性:name=Bob, age=25
print("恢复对象方法调用:", p_restored.greet())
# 运行结果:恢复对象方法调用: Hello, I'm Bob, 25 years old.
七、常见不可序列化对象
并非所有对象都能被pickle处理,以下是典型反例:
1. 打开的文件句柄
文件句柄(如open()返回的对象)关联操作系统资源,无法被序列化:
python
import pickle
# 打开一个文件句柄
file = open("test.txt", "w")
try:
pickle.dumps(file) # 尝试序列化文件句柄
except TypeError as e:
print("错误信息:", e)
# 运行结果:错误信息: cannot pickle '_io.TextIOWrapper' object
2. lambda函数
lambda函数是匿名的,pickle无法捕获其定义上下文:
python
import pickle
# 定义lambda函数
add = lambda x, y: x + y
try:
pickle.dumps(add) # 尝试序列化lambda函数
except TypeError as e:
print("错误信息:", e)
# 运行结果:错误信息: Can't pickle <function <lambda> at 0x7f8b6c0d5d30>: attribute lookup <lambda> on __main__ failed
八、异常处理
pickle的操作可能抛出以下异常(均继承自PickleError基类):
| 异常类型 | 触发场景 |
|---|---|
PicklingError |
序列化(dump/dumps)时遇到无法处理的对象 |
UnpicklingError |
反序列化(load/loads)时遇到格式错误的数据 |
示例:捕获序列化异常
python
import pickle
class UnpicklableClass:
def __getstate__(self):
raise ValueError("This object cannot be pickled")
obj = UnpicklableClass()
try:
pickle.dumps(obj)
except pickle.PicklingError as e:
print("序列化失败:", e)
# 运行结果:序列化失败: Can't pickle <__main__.UnpicklableClass object at 0x7f8b6c0d5f70>: This object cannot be pickled
九、安全注意
重要警告 :pickle反序列化不可信数据时存在严重安全风险!
因为pickle在反序列化过程中会自动执行对象的__reduce__方法,恶意构造的字节流可能触发任意代码执行(类似执行eval())。
安全实践:
- 仅反序列化来自可信来源(如自己生成的)的数据;
- 若需跨语言或传输不可信数据,优先选择JSON、Protocol Buffers等更安全的格式。
十、练习任务
任务1:字典的持久化存储
将字典{"user": "admin", "permissions": ["read", "write"]}序列化到文件user.pkl,再读取并验证数据是否一致。
参考代码:
python
import pickle
data = {"user": "admin", "permissions": ["read", "write"]}
# 写入文件
with open("user.pkl", "wb") as f:
pickle.dump(data, f)
# 读取文件
with open("user.pkl", "rb") as f:
restored_data = pickle.load(f)
print("原始数据:", data)
print("恢复数据:", restored_data)
assert data == restored_data # 验证一致性
预期输出:
arduino
原始数据: {'user': 'admin', 'permissions': ['read', 'write']}
恢复数据: {'user': 'admin', 'permissions': ['read', 'write']}
任务2:自定义类实例的序列化与验证
定义一个Book类(包含title和author属性),创建实例后序列化到文件book.pkl,再读取并验证属性是否一致。
参考代码:
python
import pickle
class Book:
def __init__(self, title, author):
self.title = title
self.author = author
def info(self):
return f"《{self.title}》 by {self.author}"
# 创建实例
book = Book("Python编程从入门到精通", "John Smith")
# 序列化
with open("book.pkl", "wb") as f:
pickle.dump(book, f)
# 反序列化
with open("book.pkl", "rb") as f:
restored_book = pickle.load(f)
print("原始对象信息:", book.info())
print("恢复对象信息:", restored_book.info())
assert book.title == restored_book.title
assert book.author == restored_book.author
预期输出:
csharp
原始对象信息: 《Python编程从入门到精通》 by John Smith
恢复对象信息: 《Python编程从入门到精通》 by John Smith
通过本文的学习,你已经掌握了pickle模块的核心用法。
在实际开发中,需根据场景权衡其便利性与安全性------对于可信数据的持久化,pickle是高效的工具;
但对于网络传输或不可信数据,建议选择更安全的序列化方案。
最后感谢阅读!欢迎关注我,微信公众号: 倔强青铜三 。
点赞、收藏、关注 ,一键三连!!欢迎 点击 【👍喜欢作者】 按钮进行 打赏💰💰,请我喝一杯咖啡☕️!