在 Python 编程世界里,数据的存储与传输是常见需求。pickle
模块作为 Python 标准库的一员,承担着对象序列化和反序列化的重任。本文将深入探索pickle
模块,带你从基础概念到实际应用,全面掌握这一强大工具,无论是数据持久化、网络传输,还是复杂对象处理,都能游刃有余。
序列化,简单来说,就是把 Python 中的各种复杂对象(像列表、字典、自定义类的实例等)转化成一种可以存储或传输的格式,这个过程就好比把一堆物品打包整理,方便搬运和存放。而反序列化则是反向操作,将存储或传输的数据还原成原来的对象,如同打开包裹,取出里面的物品并恢复原样。pickle
模块在 Python 中专门负责这两项操作,“pickling” 指的是序列化,“unpickling” 就是反序列化 。
使用pickle
时,安全性是重中之重。恶意构造的 pickle 数据在解封(反序列化)时可能执行任意代码,这就像打开了一个装满危险物品的包裹,会对系统造成严重威胁。比如,下面这个恶意示例:
import pickle
pickle.loads(b"cos\nsystem\n(S'echo hello world'\ntR.")
这段代码会导入os.system()
函数并执行字符串参数中的命令。所以,绝对不要对不信任来源的数据进行解封操作。为了保障安全,可以考虑使用hmac
对数据签名,或者在处理不信任数据时选择更安全的json
格式 。
比较项 | pickle 模块 | marshal 模块 |
---|---|---|
对象引用处理 | 跟踪已序列化对象,共享对象和递归对象处理良好 | 不跟踪,递归对象会使解释器崩溃,不支持对象共享 |
用户定义类及实例支持 | 能存储和还原类实例,但类定义需可导入 | 不支持序列化用户定义类及其实例 |
跨版本兼容性 | 选择合适协议可实现跨版本兼容 | 格式不保证移植性,主要服务于.pyc 文件,可能破坏向后兼容 |
比较项 | pickle 模块 | json 模块 |
---|---|---|
数据格式 | 二进制格式 | 文本格式(输出 unicode 文本,常以 utf - 8 编码) |
可读性 | 不可直观阅读 | 可直观阅读 |
互操作性 | Python 专用 | 广泛用于 Python 系统之外,可互操作 |
数据类型支持 | 能表示大量 Python 数据类型,包括自定义类 | 只能表示 Python 内置类型的子集,默认不支持自定义类 |
安全性风险 | 反序列化不信任数据可能执行任意代码 | 反序列化不信任数据本身不会造成此漏洞 |
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)
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 加入,支持带外数据,加速带内数据处理 |
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。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
定义)、模块最高层级的类,以及满足特定条件的类实例 。
类可以通过定义特殊方法来控制实例的序列化和反序列化行为:
__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
表。copyreg
模块管理全局dispatch
表,可创建自定义dispatch
表来定制特定类的序列化过程 。
通过__getstate__()
和__setstate__()
方法,可修改类的封存行为,实现有状态对象在序列化和反序列化过程中的状态保存与恢复 。
当dispatch_table
不够灵活时,可子类化Pickler
类并实现reducer_override()
方法,基于对象类型以外的规则定制封存 。
在处理海量数据传输时,为减少内存复制,可利用pickle
第 5 版及以上协议的外部传输功能。数据提供方需实现特定的__reduce_ex__()
方法返回PickleBuffer
实例,使用方通过传递buffer_callback
和buffers
参数来优化数据传输 。
默认情况下,解封会导入 pickle 数据中的类或函数,存在安全风险。可通过定制Unpickler.find_class()
方法,限制解封的全局对象,只允许加载安全的类或函数 。
pickle
模块在 Python 对象序列化和反序列化方面功能强大,但使用时要格外注意安全性。通过本文对其基础概念、使用方法、高级应用以及安全限制的详细讲解,希望你能熟练掌握这一工具,在数据处理、存储和传输场景中灵活运用。在实际项目中,根据具体需求选择合适的协议版本、正确处理类实例和外部对象,合理利用各种定制功能,确保程序高效、安全地运行。
Python、pickle 模块、对象序列化、反序列化、数据存储、数据传输、安全编程
pickle
模块全面且权威的介绍,从基础概念到高级应用,包含大量代码示例。pickle
模块的讲解深入,结合实际案例,帮助读者更好地理解和运用该模块,提升 Python 编程能力。