Python——cPickle

持久性就是指保存对象,甚至在多次执行同一程序之间也保持对象。通过本文,您会对Python对象的各种持久性机制(从关系数据库到Python得pickle以及其它机制)有一个总体认识。另外,还会让您更深一步地了解Python的对象序列化能力。


什么是持久性


持久性的基本思想很简单。假定有一个Pyhont程序,它可能是一个管理日常待办事项的程序,你希望在多次执行这个程序之间可以保存应用程序对象(待办事项)。换句话说,你希望将对象存储在磁盘,便于以后检索。这就是持久性。要达到这个目的,有几种方法,每一种方法都有其优缺点。


例如,可以将对象数据存储在某格式的文本文件中,譬如 CSV 文件。或者可以用关系数据库,譬如 MySQL、PostgreSQL 或者 DB2。这些文件格式和数据库都非常优秀,对于所有这些存储机制,Python 都有健壮的接口。


这些存储机制都有一个共同点:存储的数据是独立于对这些数据进行操作的对象和程序。这样做的好处是,数据可以作为共享的资源,供其它应用程序使用。缺点是,使用这种方式,可以允许其它程序访问对象的数据,这违背了面向对象的封装性原则——即对象的数据只能通过这个对象自身的公共接口来访问。


另外,对于某些应用程序,关系数据库方法可能不是很理想。尤其是,关系数据库不理解对象。相反,关系数据库会强行使用自己的类型系统和关系数据模型(表),每张表包含一个元组(行),每行包含具有固定数目的静态类型字段(列)。如果应用程序的对象模型不能够方便地转换到关系模型,那么在将对象映射到元组以及将元组映射回对象方面,会碰到一定难度。这种困难常被成为阻碍性不匹配问题。


对象持久性


如果希望透明地存储 Python 对象,而不丢失其身份和类型等信息,则需要某种形式的对象序列化:它是一个将任意复杂的对象转成对象的文本或二进制表示的过程。同样,必须能够将对象经过序列化后的形式回复到原有的对象。在 Python 中, 这种序列化过程称为 pickle,可以将对象pickle 成字符串、磁盘上的文件或类似于文件的对象,也可以将这些字符串、文件或任何类似于文件的对象unpickle 成原来的对象。


假定您喜欢将任何事物都保存成对象,并且希望避免将对象转换成某种基于非对象存储的开销;那么 pickle 文件可以提供这些好处,但有时可能需要比这种简单 pickle 文件更健壮以及更具有可收缩性的事物。例如,只用pickle不能解决命名和查找pickle文件这样的问题,另外,他也不能支持并发地访问持久性对象。如果需要这些方面的功能,则要求助与ZODN(针对Python的Z对象数据库)这类数据库。ZODB是一个健壮的、多用户和面向对象的数据库系统,它能够存储和管理人已复杂的Python对象,并支持事物操作和并发控制。令人足够感兴趣的是,甚至ZODB也依靠Python的本机序列化能力,而且要有效地使用ZODB,必须充分了解pickle。


既然我们已经简要讨论了存储持久对象的各种方法,那么现在该详细探讨pickle过程了。虽然我们主要感兴趣的是探索以各种方式来保存Python对象,而不必将其转换成某种其他格式,但我们仍然有一些需要关注的地方,譬如:如何有效地pickle和unpickle简单对象以及复杂对象,包括定制类的实例;如何维护对象的引用,包括循环引用和递归引用;以及如何处理类定义发生的变化,从而使用以前经过pickle的实例时不会发生问题。我们将在随后关于Python的pickle能力探讨中涉及所有这些问题。


pickle模块以及同类模块cPickle想Python提供了pickle支持。后者使用C编码的,它具有更好的性能,对于大多数应用程序,推荐使用该模块。我们将继续讨论pickle,但本文的示例实际是利用了cPickle。由于其中大多数示例要用Python Shell来显示,所以先展示一下如何导入cPickle,并可以作为pickle来引用它:


>>> import cPickle as pickle


现在已经导入了该模块,接下来让我们看一下pickle接口。pickle模块提供了一下函数对:dumps(object)返回一个字符串,它包含了一个pickle格式的对象;loads(string)返回包含在pikcle字符串中的对象;dump(object,file)将对象写到文件,这个文件可以是实际的物理文件,但也可以是任何类似于文件的对象,这个对象具有write()方法,可以接受单个的字符串函数;load(file)返回包含在pickle文件的对象。


缺省情况下,dumps()和dump()使用可打印的ASCII表示来创建pickle。两者都有一个final参数(可选),如果为True,则该参数指定用更快已经更小的二进制表示来创建pickle。loads()和load()函数自动检测pickle是二进制格式还是文本格式。


清单 1 显示了一个交互式回话,这里使用了刚才描述的dumps() 和 loads() 函数:


清单 1.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(),它们使用文件和类似文件的对象。这些函数的操作非常类似于我们刚才所看到的dumps()和loads(),区别在于它们还有另外一种能力——dump()函数能一个接着一个地将几个对象转储到同一个文件。随后调用load()来以同样的顺序检索这些对象。


清单 2. dump() 和 load() 示例


>>> 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()



















这里是可爱的传送门

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值