苦练Python第73天:玩转对象持久化,pickle模块极速入门

前言

大家好,我是 倔强青铜三 。欢迎关注我,微信公众号: 倔强青铜三。点赞、收藏、关注,一键三连!

欢迎来到 苦练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类(包含titleauthor属性),创建实例后序列化到文件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是高效的工具;

但对于网络传输或不可信数据,建议选择更安全的序列化方案。

最后感谢阅读!欢迎关注我,微信公众号: 倔强青铜三
点赞、收藏、关注 ,一键三连!!

欢迎 点击 【👍喜欢作者】 按钮进行 打赏💰💰,请我喝一杯咖啡☕️!

相关推荐
咕咚-萌西8 小时前
DeepSeek-OCR
人工智能·深度学习·ocr
xcbeyond8 小时前
从 MCP 到 RAG 再到 Agent:AI 应用架构的下一次跃迁
人工智能
Godspeed Zhao8 小时前
自动驾驶中的传感器技术74——Navigation(11)
人工智能·机器学习·自动驾驶
Godspeed Zhao9 小时前
自动驾驶中的传感器技术75——Navigation(12)
人工智能·机器学习·自动驾驶
程序员三藏9 小时前
Postman持久化保存/设置断言详解
自动化测试·软件测试·python·测试工具·职场和发展·接口测试·postman
rengang669 小时前
04-深度学习的基本概念:涵盖深度学习中的关键术语和原理
人工智能·深度学习
杨成功9 小时前
大语言模型(LLM)学习笔记
人工智能·llm
java1234_小锋9 小时前
PyTorch2 Python深度学习 - 卷积神经网络(CNN)介绍实例 - 使用MNIST识别手写数字示例
python·深度学习·cnn·pytorch2
雍凉明月夜9 小时前
人工智能学习中深度学习之python基础之迭代器、生成器、文件处理和模块等
python·深度学习·学习·pycharm