Python持久化管理pickle模块笔记

    xiaoxiao2025-04-09  16

    引用部分来自:http://blog.csdn.net/jq0123/article/details/4319589

    http://www.cnblogs.com/cobbliu/archive/2012/09/04/2670178.html


    Programming Python, 3rd Edition 翻译 最新版本见:http://wiki.woodpecker.org.cn/moin/PP3eD

    什么是持久化

    持久性就是指保持对象,甚至在多次执行同一程序之间也保持对象。

    Pickle对象

    pickle是为了序列化/反序列化一个对象的,可以把一个对象持久化存储。 比如你有一个对象,想下次运行程序的时候直接用,可以直接用pickle打包存到硬盘上。或者你想把一个对象传给网络上的其他程序,可以用pickle打包,然后传过去,那边的python程序用pickle反序列化,就可以用了。 用法上,它主要有两个函数:load和dump,load是从序列化之后的数据中解出来,dump是把对象序列化。

    Python系统的标准部件,pickle模块。它可以将几乎任意的Python内存对象,转换为单一线性的字符串格式,**使之适于无格式文件存储**,或在可靠来源之间跨越网络套接口传输等等,并可反向转换。这种从对象到字符串的转换通常被称为**序列化**(serialization):将内存中的任意数据结构映射为串行字符串形式。 对象的字符串表达由于其线性的格式,有时也被称为字节流。它包含了原始内存中对象的所有内容和引用结构。当对象后来从其字节串重建时,内存中新建的对象与原对象具有相同的结构和值,但位于不同的内存地址。该重建对象实际上是**原对象的复制**。 Pickle可用于几乎所有的Python数据类型:数字、列表、字典、类实例、嵌套结构,等等,因此**它是存储数据的通用方法**。因为pickle包含的是Python本地对象,所以几乎没有数据库的API;对象存储与处理及后来的提取用的都是通常的Python语法。

    使用对象pickle

    第一次听到pickle,可能觉得有点复杂,但好消息是,Python隐藏了所有从对象到字符串转换的复杂性。事实上,pickle模块的接口简单易用,简直令人难以置信。例如,要pickle对象到一个序列化字符串,我们可以生成一个pickler,并调用其方法,或使用模块中的便捷函数来达到相同的效果:

    dump把pickler序列化

    生成一个新的pickler,用来pickle到一个打开的输出文件对象file:

    P = pickle.Pickler( file)

    写一个对象到pickler的文件/流:

    P.dump( object)

    等同于上两个调用的组合:pickle对象到一个打开的文件:

    pickle.dump( object, file)

    返回一个字符串作为已pickle对象的表达:

    string = pickle.dumps( object)

    load将pickler反序列化

    从一个序列化字符串unpickle回原始对象是类似的,可以用对象也可以用便捷函数接口:

    生成一个unpickler,用来从一个打开的文件对象file unpickle:

    U = pickle.Unpickler( file)

    从unpickler的文件/流读取一个对象:

    object = U.load( )

    等同于上两个调用的组合:从一个打开的文件unpickle一个对象:

    object = pickle.load( file)

    从字符串读取一个对象,而不是从文件:

    object = pickle.loads( string)

    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() 的演示

    >>> import cPickle as pickle >>> t1 = ('this is a string', 42, [1, 2, 3], None) >>> t1 ('this is a string', 42, [1, 2, 3], None) >>> p1 = pickle.dumps(t1) >>> p1 "(S'this is a string'/nI42/n(lp1/nI1/naI2/naI3/naNtp2/n." >>> print p1 (S'this is a string' I42 (lp1 I1 aI2 aI3 aNtp2 . >>> t2 = pickle.loads(p1) >>> t2 ('this is a string', 42, [1, 2, 3], None) >>> p2 = pickle.dumps(t1, True) >>> p2 '(U/x10this is a stringK*]q/x01(K/x01K/x02K/x03eNtq/x02.' >>> t3 = pickle.loads(p2) >>> t3 ('this is a string', 42, [1, 2, 3], None)

    注:该文本 pickle 格式很简单,这里就不解释了。事实上,在 pickle 模块中记录了所有使用的约定。我们还应该指出,在我们的示例中使用的都是简单对象,因此使用二进制 pickle 格式不会在节省空间上显示出太大的效率。然而,在实际使用复杂对象的系统中,您会看到,使用二进制格式可以在大小和速度方面带来显著的改进。

    dump() 和 load() 示例

    这些示例用到了 dump() 和 load() ,它们使用文件和类似文件的对象。这些函数的操作非常类似于我们刚才所看到的 dumps() 和 loads() ,区别在于它们还有另一种能力 — dump() 函数能一个接着一个地将几个对象转储到同一个文件。随后调用 load() 来以同样的顺序检索这些对象。清单 2 显示了这种能力的实际应用:

    >>> a1 = 'apple' >>> b1 = {1: 'One', 2: 'Two', 3: 'Three'} >>> c1 = ['fee', 'fie', 'foe', 'fum'] >>> f1 = file('temp.pkl', 'wb') >>> pickle.dump(a1, f1, True) >>> pickle.dump(b1, f1, True) >>> pickle.dump(c1, f1, True) >>> f1.close() >>> f2 = file('temp.pkl', 'rb') >>> a2 = pickle.load(f2) >>> a2 'apple' >>> b2 = pickle.load(f2) >>> b2 {1: 'One', 2: 'Two', 3: 'Three'} >>> c2 = pickle.load(f2) >>> c2 ['fee', 'fie', 'foe', 'fum'] >>> f2.close()

    检索所支持的格式

    >>> pickle.format_version '1.3' >>> pickle.compatible_formats ['1.0', '1.1', '1.2']

    对象引用的维护

    在 Python 中,变量是对象的引用。同时,也可以用多个变量引用同一个对象。经证明,Python 在用经过 pickle 的对象维护这种行为方面丝毫没有困难

    >>> a = [1, 2, 3] >>> b = a >>> a [1, 2, 3] >>> b [1, 2, 3] >>> a.append(4) >>> a [1, 2, 3, 4] >>> b [1, 2, 3, 4] >>> c = pickle.dumps((a, b)) >>> d, e = pickle.loads(c) >>> d [1, 2, 3, 4] >>> e [1, 2, 3, 4] >>> d.append(5) >>> d [1, 2, 3, 4, 5] >>> e [1, 2, 3, 4, 5]

    递归引用

    >>> l = [1, 2, 3] >>> l.append(l) >>> l [1, 2, 3, [...]] >>> l[3] [1, 2, 3, [...]] >>> l[3][3] [1, 2, 3, [...]] >>> p = pickle.dumps(l) >>> l2 = pickle.loads(p) >>> l2 [1, 2, 3, [...]] >>> l2[3] [1, 2, 3, [...]] >>> l2[3][3] [1, 2, 3, [...]]

    循环引用

    >>> a = [1, 2] >>> b = [3, 4] >>> a.append(b) >>> a [1, 2, [3, 4]] >>> b.append(a) >>> a [1, 2, [3, 4, [...]]] >>> b [3, 4, [1, 2, [...]]] >>> a[2] [3, 4, [1, 2, [...]]] >>> b[2] [1, 2, [3, 4, [...]]] >>> a[2] is b 1 >>> b[2] is a 1 >>> f = file('temp.pkl', 'w') >>> pickle.dump((a, b), f) >>> f.close() >>> f = file('temp.pkl', 'r') >>> c, d = pickle.load(f) >>> f.close() >>> c [1, 2, [3, 4, [...]]] >>> d [3, 4, [1, 2, [...]]] >>> c[2] [3, 4, [1, 2, [...]]] >>> d[2] [1, 2, [3, 4, [...]]] >>> c[2] is d 1 >>> d[2] is c 1

    分别 pickle vs. 在一个元组中一起 pickle

    注意,如果分别 pickle 每个对象,而不是在一个元组中一起 pickle 所有对象,会得到略微不同(但很重要)的结果,如下所示:

    >>> f = file('temp.pkl', 'w') >>> pickle.dump(a, f) >>> pickle.dump(b, f) >>> f.close() >>> f = file('temp.pkl', 'r') >>> c = pickle.load(f) >>> d = pickle.load(f) >>> f.close() >>> c [1, 2, [3, 4, [...]]] >>> d [3, 4, [1, 2, [...]]] >>> c[2] [3, 4, [1, 2, [...]]] >>> d[2] [1, 2, [3, 4, [...]]] >>> c[2] is d 0 >>> d[2] is c 0

    作为原来对象副本的被恢复的对象

    >>> j = [1, 2, 3] >>> k = j >>> k is j 1 >>> x = pickle.dumps(k) >>> y = pickle.loads(x) >>> y [1, 2, 3] >>> y == k 1 >>> y is k 0 >>> y is j 0 >>> k is j 1

    维护分别 pickle 的对象间的引用

    >>> f = file('temp.pkl', 'w') >>> pickler = pickle.Pickler(f) >>> pickler.dump(a) <cPickle.Pickler object at 0x89b0bb8> >>> pickler.dump(b) <cPickle.Pickler object at 0x89b0bb8> >>> f.close() >>> f = file('temp.pkl', 'r') >>> unpickler = pickle.Unpickler(f) >>> c = unpickler.load() >>> d = unpickler.load() >>> c[2] [3, 4, [1, 2, [...]]] >>> d[2] [1, 2, [3, 4, [...]]] >>> c[2] is d 1 >>> d[2] is c 1

    试图 pickle 文件对象的结果

    一些对象类型是不可 pickle 的。例如,Python 不能 pickle 文件对象(或者任何带有对文件对象引用的对象),因为 Python 在 unpickle 时不能保证它可以重建该文件的状态(另一个示例比较难懂,在这类文章中不值得提出来)。试图 pickle 文件对象会导致以下错误:

    >>> f = file('temp.pkl', 'w') >>> p = pickle.dumps(f) Traceback (most recent call last): File "<input>", line 1, in ? File "/usr/lib/python2.2/copy_reg.py", line 57, in _reduce raise TypeError, "can't pickle %s objects" % base.__name__ TypeError: can't pickle file objects

    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错误:

    >>> f = open('temp', 'w') # text mode file on Windows >>> pickle.dump(('ex', 'parrot'), f) # use default text protocol >>> f.close( ) >>> >>> pickle.load(open('temp', 'r')) # OK in text mode ('ex', 'parrot') >>> pickle.load(open('temp', 'rb')) # fails in binary Traceback (most recent call last): File "<pyshell#337>", line 1, in -toplevel- pickle.load(open('temp', 'rb')) ...lines deleted... ValueError: insecure string pickle

    回避这个潜在问题的方法之一是,总是使用二进制模式的文件,即使是用文本pickle协议。至少对于二进制pickler协议(高于默认0),您必须以二进制模式打开文件,所以这不是一个坏习惯:

    >>> f = open('temp', 'wb') # create in binary mode >>> pickle.dump(('ex', 'parrot'), f) # use text protocol >>> f.close( ) >>> >>> pickle.load(open('temp', 'rb')) ('ex', 'parrot') >>> pickle.load(open('temp', 'r')) ('ex', 'parrot')

    请参考Python库手册,以了解更多pickler的信息。另外,请查阅marshal,它也是一个序列化对象的模块,但只能处理简单对象类型。pickle比marshal更通用,并通常是首选。

    而当你翻看(或点击)Python手册时,请一定也要看看cPickle模块的条目,它是pickle的C语言实现,性能上更快。您可以显式导入cPickle替代pickle,以大幅提升速度;其主要的限制是,你不能继承该版本的Pickle和Unpickle,因为它们是函数,而不是类(多数程序并不要求它们是类)。pickle和cPickle模块使用兼容的数据格式,所以它们可以互换使用。

    如果您的Python中有shelve模块,它会自动选用cPickle模块,而不是pickle,以达到更快的序列化。我还没有解释过shelve,但我马上就会讲到它。

    转载请注明原文地址: https://ju.6miu.com/read-1297889.html
    最新回复(0)