Python 对象序列化神器 ——pickle 深度剖析
在 Python 编程世界里,数据的存储与传输是常见需求。pickle
模块作为 Python 标准库的一员,承担着对象序列化和反序列化的重任。本文将深入探索pickle
模块,带你从基础概念到实际应用,全面掌握这一强大工具,无论是数据持久化、网络传输,还是复杂对象处理,都能游刃有余。
文章目录
一、pickle 模块基础概念
(一)什么是序列化与反序列化
序列化,简单来说,就是把 Python 中的各种复杂对象(像列表、字典、自定义类的实例等)转化成一种可以存储或传输的格式,这个过程就好比把一堆物品打包整理,方便搬运和存放。而反序列化则是反向操作,将存储或传输的数据还原成原来的对象,如同打开包裹,取出里面的物品并恢复原样。pickle
模块在 Python 中专门负责这两项操作,“pickling” 指的是序列化,“unpickling” 就是反序列化 。
(二)pickle 模块的安全性问题
使用pickle
时,安全性是重中之重。恶意构造的 pickle 数据在解封(反序列化)时可能执行任意代码,这就像打开了一个装满危险物品的包裹,会对系统造成严重威胁。比如,下面这个恶意示例:
import pickle
pickle.loads(b"cos\nsystem\n(S'echo hello world'\ntR.")
这段代码会导入os.system()
函数并执行字符串参数中的命令。所以,绝对不要对不信任来源的数据进行解封操作。为了保障安全,可以考虑使用hmac
对数据签名,或者在处理不信任数据时选择更安全的json
格式 。
(三)与其他模块的比较
- 与 marshal 模块的区别
比较项 pickle 模块 marshal 模块 对象引用处理 跟踪已序列化对象,共享对象和递归对象处理良好 不跟踪,递归对象会使解释器崩溃,不支持对象共享 用户定义类及实例支持 能存储和还原类实例,但类定义需可导入 不支持序列化用户定义类及其实例 跨版本兼容性 选择合适协议可实现跨版本兼容 格式不保证移植性,主要服务于.pyc 文件,可能破坏向后兼容 - 与 json 模块的区别
比较项 pickle 模块 json 模块 数据格式 二进制格式 文本格式(输出 unicode 文本,常以 utf - 8 编码) 可读性 不可直观阅读 可直观阅读 互操作性 Python 专用 广泛用于 Python 系统之外,可互操作 数据类型支持 能表示大量 Python 数据类型,包括自定义类 只能表示 Python 内置类型的子集,默认不支持自定义类 安全性风险 反序列化不信任数据可能执行任意代码 反序列化不信任数据本身不会造成此漏洞
二、pickle 模块的使用方法
(一)模块接口函数
- dump 和 dumps 函数
pickle.dump(obj, file, protocol=None, *, fix_imports=True, buffer_callback=None)
:将对象obj
序列化后写入已打开的文件对象file
。例如:
import pickle
data = {'name': 'Alice', 'age': 30}
with open('data.pkl', 'wb') as f:
pickle.dump(data, f)
pickle.dumps(obj, protocol=None, *, fix_imports=True, buffer_callback=None)
:把对象obj
序列化后直接返回bytes
类型数据,而不是写入文件。如:
data = {'name': 'Bob', 'age': 25}
pickled_data = pickle.dumps(data)
print(pickled_data)
- load 和 loads 函数
pickle.load(file, *, fix_imports=True, encoding='ASCII', errors='strict', buffers=None)
:从已打开的文件对象file
中读取序列化数据,重建对象层次结构并返回。例如:
with open('data.pkl', 'rb') as f:
loaded_data = pickle.load(f)
print(loaded_data)
pickle.loads(data, /, *, fix_imports=True, encoding='ASCII', errors='strict', buffers=None)
:根据传入的bytes
类型数据data
,重建对象层级结构并返回。比如:
pickled_data = pickle.dumps({'name': 'Charlie', 'age': 35})
new_data = pickle.loads(pickled_data)
print(new_data)
(二)协议版本
pickle
模块目前有 6 种协议版本:
协议版本 | 特点 | 兼容性 |
---|---|---|
v0 | 原始 “人类可读” 协议 | 向后兼容早期 Python 版本 |
v1 | 较早的二进制格式 | 与早期 Python 版本兼容 |
v2 | Python 2.3 引入,为新式类提供更高效封存机制 | |
v3 | Python 3.0 引入,显式支持 bytes 字节对象 | 不能用 Python 2.x 解封,是 Python 3.0 - 3.7 的默认协议 |
v4 | Python 3.4 添加,支持存储大对象,优化数据格式 | 是 Python 3.8 的默认协议 |
v5 | Python 3.8 加入,支持带外数据,加速带内数据处理 |
(三)Pickler 和 Unpickler 类
- Pickler 类
pickle.Pickler(file, protocol=None, *, fix_imports=True, buffer_callback=None)
:用于创建一个序列化对象,接受一个二进制文件用于写入 pickle 数据流。比如:
import pickle
import io
data = [1, 2, 3]
f = io.BytesIO()
p = pickle.Pickler(f, protocol=4)
p.dump(data)
dump(obj)
:将对象obj
序列化后写入已打开的文件对象。persistent_id(obj)
:可被子类重写,用于处理外部对象的持久化 ID。dispatch_table
:用于注册 reduction 函数,可自定义序列化过程。reducer_override(obj)
:在子类中定义,优先级高于dispatch_table
中的 reducer。
- Unpickler 类
pickle.Unpickler(file, *, fix_imports=True, encoding='ASCII', errors='strict', buffers=None)
:用于创建一个反序列化对象,接受一个二进制文件用于读取 pickle 数据流。例如:
import pickle
import io
f = io.BytesIO(b'\x80\x04\x95\x0f\x00\x00\x00\x00\x00\x00\x00]\x94(K\x01K\x02K\x03e.')
u = pickle.Unpickler(f)
loaded_data = u.load()
print(loaded_data)
load()
:从文件对象中读取序列化数据,重建对象层次结构并返回。persistent_load(pid)
:用于处理持久化 ID 对应的对象。find_class(module, name)
:可被子类重写,控制加载对象的类型和方式,增强安全性。
(四)可被序列化 / 反序列化的对象
能被pickle
处理的对象包括:内置常量(如None
、True
、False
等)、数值类型(整数、浮点数、复数)、字符串、字节串、字节数组、包含可序列化对象的容器(元组、列表、集合、字典 )、模块最高层级的函数(用def
定义)、模块最高层级的类,以及满足特定条件的类实例 。
三、pickle 模块的高级应用
(一)封存类实例
类可以通过定义特殊方法来控制实例的序列化和反序列化行为:
__getnewargs_ex__()
:用于控制解封时传给__new__()
方法的参数,返回(args, kwargs)
对。__getnewargs__()
:类似__getnewargs_ex__()
,但只支持位置参数,返回一个tuple
类型的args
。__getstate__()
:重写该方法可自定义实例被序列化的内容。__setstate__ (state)
:在解封时被调用,用于恢复实例状态。__reduce__()
:功能强大但易出错,返回字符串或元组,用于定义对象的序列化方式。__reduce_ex__(protocol)
:与__reduce__()
类似,但接受协议版本参数,可覆盖__reduce__()
的行为 。
(二)持久化外部对象
通过持久化 ID,pickle
可以引用已封存数据流之外的对象。发送端的Pickler
需实现persistent_id()
方法返回对象的持久化 ID,接收端的Unpickler
要实现persistent_load()
方法根据 ID 返回对象 。
(三)Dispatch 表
当需要对某些类进行自定义序列化,又不想在类中添加代码时,可使用dispatch
表。copyreg
模块管理全局dispatch
表,可创建自定义dispatch
表来定制特定类的序列化过程 。
(四)处理有状态的对象
通过__getstate__()
和__setstate__()
方法,可修改类的封存行为,实现有状态对象在序列化和反序列化过程中的状态保存与恢复 。
(五)类型、函数和其他对象的自定义归约
当dispatch_table
不够灵活时,可子类化Pickler
类并实现reducer_override()
方法,基于对象类型以外的规则定制封存 。
(六)外部缓冲区
在处理海量数据传输时,为减少内存复制,可利用pickle
第 5 版及以上协议的外部传输功能。数据提供方需实现特定的__reduce_ex__()
方法返回PickleBuffer
实例,使用方通过传递buffer_callback
和buffers
参数来优化数据传输 。
四、限制全局变量
默认情况下,解封会导入 pickle 数据中的类或函数,存在安全风险。可通过定制Unpickler.find_class()
方法,限制解封的全局对象,只允许加载安全的类或函数 。
总结
pickle
模块在 Python 对象序列化和反序列化方面功能强大,但使用时要格外注意安全性。通过本文对其基础概念、使用方法、高级应用以及安全限制的详细讲解,希望你能熟练掌握这一工具,在数据处理、存储和传输场景中灵活运用。在实际项目中,根据具体需求选择合适的协议版本、正确处理类实例和外部对象,合理利用各种定制功能,确保程序高效、安全地运行。
TAG
Python、pickle 模块、对象序列化、反序列化、数据存储、数据传输、安全编程
相关学习资源:
- Tekin的Python编程秘籍库: Python 实用知识与技巧分享,涵盖基础、爬虫、数据分析等干货 本 Python 专栏聚焦实用知识,深入剖析基础语法、数据结构。分享爬虫、数据分析等热门领域实战技巧,辅以代码示例。无论新手入门还是进阶提升,都能在此收获满满干货,快速掌握 Python 编程精髓。
- Python 官方文档:https://docs.python.org/zh-cn/3.12/library/pickle.html ,本文的主要参考资料,提供了
pickle
模块全面且权威的介绍,从基础概念到高级应用,包含大量代码示例。
- 《Python 核心编程》:书籍详细介绍了 Python 的各类核心知识,其中对
pickle
模块的讲解深入,结合实际案例,帮助读者更好地理解和运用该模块,提升 Python 编程能力。