python的pickle模块实现了基本的数据序列和反序列化。通过pickle模块的序列化操作我们能够将程序中运行的对象信息保存到文件中去,永久存储;通过pickle模块的反序列化操作,我们能够从文件中创建上一次程序保存的对象
1、什么东西能用pickle模块存储?
所有Python支持的 原生类型 : 布尔, 整数, 浮点数, 复数, 字符串, bytes(字节串)对象, 字节数组, 以及 None.
由任何原生类型组成的列表,元组,字典和集合。
由任何原生类型组成的列表,元组,字典和集合组成的列表,元组,字典和集合(可以一直嵌套下去,直至Python支持的最大递归层数).
函数,类,和类的实例(带警告)。
【摘自《深入Python》】
2、示例(保存数据到PICKLE文件):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
#encoding:UTF-8
import
pickle
#序列化
def
dump_pickle():
user
=
{}
user[
'id'
]
=
1
user[
'name'
]
=
'tanweijie'
user[
'email'
]
=
'tanweijie@outlook.com'
user[
'sex'
]
=
'boy'
#with保证自动关闭文件
#设置文件模式为'wb'来以二进制写模式打开文件
with
open
(
'C:/Users/Mr_Tank_/Desktop/user.pickle'
,
'wb'
) as f:
#dump()函数接受一个可序列化的Python数据结构
pickle.dump(user,f)
print
(
'success'
)
#反序列化
def
load_pickle():
with
open
(
'C:/Users/Mr_Tank_/Desktop/user.pickle'
,
'rb'
) as f:
user
=
pickle.load(f)
#user变量是一个字典
print
(user)
|
3、结果:
1
2
3
4
|
>>> dump_pickle()
success
>>> load_pickle()
{
'id'
:
1
,
'name'
:
'tanweijie'
,
'sex'
:
'boy'
,
'email'
:
'tanweijie@outlook.com'
}
|
使用对象pickle
第一次听到pickle,可能觉得有点复杂,但好消息是,Python隐藏了所有从对象到字符串转换的复杂性。事实上,pickle模块的接口简单易用,简直令人难以置信。例如,要pickle对象到一个序列化字符串,我们可以生成一个pickler,并调用其方法,或使用模块中的便捷函数来达到相同的效果:
dump把pickler序列化
生成一个新的pickler,用来pickle到一个打开的输出文件对象file:
- 1
写一个对象到pickler的文件/流:
- 1
等同于上两个调用的组合:pickle对象到一个打开的文件:
- 1
返回一个字符串作为已pickle对象的表达:
- 1
load将pickler反序列化
从一个序列化字符串unpickle回原始对象是类似的,可以用对象也可以用便捷函数接口:
生成一个unpickler,用来从一个打开的文件对象file unpickle:
- 1
从unpickler的文件/流读取一个对象:
- 1
等同于上两个调用的组合:从一个打开的文件unpickle一个对象:
- 1
从字符串读取一个对象,而不是从文件:
- 1
Pickler和Unpickler是导出类。在上述所有情况下,file是个已打开的文件对象,或者是实现了以下文件对象属性的任何对象:
Pickler会调用文件的write方法,参数是个字符串。
Unpickler会调用文件的read方法,参数是字节数,以及readline,无参数。
任何提供这些属性的对象都可以作为file参数传入。特别是,file可以是一个提供了读/写方法的Python类实例(即预期的类似文件的接口)。这让您可以用类映射pickle流到内存对象,并可任意使用。例
该挂钩也可以让您通过网络传输Python对象,只要封装套接口,使之看上去像发送端pickle调用中的文件,以及像接收端unpickle调用中的文件。事实上,对一些人来说,pickle Python对象并在一个值得信赖的网络上传输,是替代如SOAP和XML-RPC之类网络传输协议的一个简单方法;只要通信的两端都有Python(被pickle的对象是用Python专有的格式表达的,而不是用XML文本)。
Pickle实战
pickle 模块提供了以下函数对: dumps(object) 返回一个字符串,它包含一个 pickle 格式的对象; loads(string) 返回包含在 pickle 字符串中的对象; dump(object, file) 将对象写到文件,这个文件可以是实际的物理文件,但也可以是任何类似于文件的对象,这个对象具有 write() 方法,可以接受单个的字符串参数; load(file) 返回包含在 pickle 文件中的对象。
缺省情况下, dumps() 和 dump() 使用可打印的 ASCII 表示来创建 pickle。两者都有一个 final 参数(可选),如果为 True ,则该参数指定用更快以及更小的二进制表示来创建 pickle。 loads() 和 load() 函数自动检测 pickle 是二进制格式还是文本格式。
dumps() 和 loads() 的演示
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
注:该文本 pickle 格式很简单,这里就不解释了。事实上,在 pickle 模块中记录了所有使用的约定。我们还应该指出,在我们的示例中使用的都是简单对象,因此使用二进制 pickle 格式不会在节省空间上显示出太大的效率。然而,在实际使用复杂对象的系统中,您会看到,使用二进制格式可以在大小和速度方面带来显著的改进。
dump() 和 load() 示例
这些示例用到了 dump() 和 load() ,它们使用文件和类似文件的对象。这些函数的操作非常类似于我们刚才所看到的 dumps() 和 loads() ,区别在于它们还有另一种能力 — dump() 函数能一个接着一个地将几个对象转储到同一个文件。随后调用 load() 来以同样的顺序检索这些对象。清单 2 显示了这种能力的实际应用:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
检索所支持的格式
- 1
- 2
- 3
- 4
对象引用的维护
在 Python 中,变量是对象的引用。同时,也可以用多个变量引用同一个对象。经证明,Python 在用经过 pickle 的对象维护这种行为方面丝毫没有困难
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
递归引用
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
循环引用
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
分别 pickle vs. 在一个元组中一起 pickle
注意,如果分别 pickle 每个对象,而不是在一个元组中一起 pickle 所有对象,会得到略微不同(但很重要)的结果,如下所示:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
作为原来对象副本的被恢复的对象
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
维护分别 pickle 的对象间的引用
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
试图 pickle 文件对象的结果
一些对象类型是不可 pickle 的。例如,Python 不能 pickle 文件对象(或者任何带有对文件对象引用的对象),因为 Python 在 unpickle 时不能保证它可以重建该文件的状态(另一个示例比较难懂,在这类文章中不值得提出来)。试图 pickle 文件对象会导致以下错误:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
Pickler协议和cPickle
在最近的Python版本中,pickler推出了协议的概念:pickle数据的保存格式。通过pickle调用时传入一个额外的参数,可指定所需的协议(但unpickle调用不需要:协议是自动从已pickle的数据确定的):
pickle.dump(object, file, protocol)
Pickle数据可以按文本协议或二进制协议产生。默认情况下,存储协议是文本协议(也称为0号协议)。在文本模式下,用来存储pickle对象的文件可以用文本模式打开,如上述的例子,并且pickle的数据是可打印的ASCII文本,并且是可读的(这基本上是对堆栈机实现的指示)。
其他协议(1号和2号协议 )以二进制格式存储pickle数据,并要求文件以二进制模式打开(例如:rb、wb)。1号协议是原始二进制格式;2号协议是Python 2.3增加的,它改善了对新型类pickle的支持。二进制格式效率更高一点,但它无法进行查看。旧的pickle调用有一个选项,即bin参数,现已被归入使用大于0的协议。pickle模块还提供了一个HIGHEST_PROTOCOL变量,传入它可以自动选择最大的协议值。
注意:如果您使用默认的文本协议,以后请务必以文本模式打开pickle文件。在一些平台上,因为Windows的行尾格式不同,以二进制模式打开文本数据可能会导致unpickle错误:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
回避这个潜在问题的方法之一是,总是使用二进制模式的文件,即使是用文本pickle协议。至少对于二进制pickler协议(高于默认0),您必须以二进制模式打开文件,所以这不是一个坏习惯:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
请参考Python库手册,以了解更多pickler的信息。另外,请查阅marshal,它也是一个序列化对象的模块,但只能处理简单对象类型。pickle比marshal更通用,并通常是首选。
而当你翻看(或点击)Python手册时,请一定也要看看cPickle模块的条目,它是pickle的C语言实现,性能上更快。您可以显式导入cPickle替代pickle,以大幅提升速度;其主要的限制是,你不能继承该版本的Pickle和Unpickle,因为它们是函数,而不是类(多数程序并不要求它们是类)。pickle和cPickle模块使用兼容的数据格式,所以它们可以互换使用。
如果您的Python中有shelve模块,它会自动选用cPickle模块,而不是pickle,以达到更快的序列化。我还没有解释过shelve,但我马上就会讲到它。