python 的pickle包学习

python基础之pickle

zope的核心模块zodb的最底层对象序列化是基于python的标准模块pickle进行的(更准确的说是cpickle),在分析zodb的实现前,我们来看看python标准模块pickle的相关知识。

1、python对象的序列化
关于python对象序列化的模块包括:pickle/cpickle、marshal、shelve,现在简单看看它们的区别和应用场合pickle是python中最通用的对象序列化包,它能跟踪python对象的引用状态,对于相同的对象只序列化一次, 而且能够发现对象的递归和共享,pickle的格式是python自定义的,同时它能处理用户自定义的对象,并能在不同平台的python解析器共享。

marshal是为了pyc文件设计的,它不能夸平台,不能处理用户自定义的对象,不能跨平台,它不是开放给开发者使用的包,而是为了方便python解析器而设计出来的。

shelve是架构在piclke的基础上的使用DBM风格封装的开发包,在一定程度上实现了多个客户程序访问数据文件,pickle是独占方式处理数据文件的。

2、piclke数据的保存格式
pickle的数据格式现在包含3个版本:
0,1,2
pickle的主要接口:
dump( obj, file[, protocol[, bin]]) protocol表示版本,bin只用在版本0下,选择保存数据方式:字符/二进制file是具有write函数的对象,它可以是file/StringIO/或者是任意自定义的就有callable write的对象
dumps( obj[, protocol[, bin]]) dumps和dump一样,只是它直接返回处理后的字符串
load( file) 从pickle数据还原python对象,它只需要一个具有read和readline函数的对象(file/StringIO/自定义对象),file对象是提供实际的数据源的对象
loads( string) 和load的作用一样,只是它直接从string还原python对象

为了方便使用,pickle模块还定义了Pickler和Unpickler对象。需要注意的是cpickle.Pickler类提供了clear_memo用于清除内存信息(而pickl使用Pickler.memo.clear())。同时cpiclke提供了Unpickler.noload函数得到对象的信息,但并不真正构建对象,在zodb中就通过这种方式得到一个对象对其他对象的引用id(persistent id)

3、pickle能处理的类型:
3.1 None, True, False
3.2 Integer, long Integer, floating point numbers, complex numbers
3.3 str, unicode str,
3.4 只包含能pickle对象的tuples, list, set dict
3.5 在模块顶层的函数和类,这是因为在pickle的时候实际上只是保存模块和函数的名称
而在unpickle的时候就把相应的模块导入
3.6 定义在顶层的build-in 函数
3.7 dict或者setstate只包含可pickle对象的实例

pickle并不是原子操作,也就是说在一个pickle调用中如果发生异常,可能部分数据已经被保存,另外如果对象处于深递归状态,那么可能超出python的最大递归深度(这个可以通过sys.setrecursionlimit()进行扩展)

4、pickle在还原对象的时候一般是不调用init函数的,如果要调用init初始化对于旧的类模型(不继承object),需要在类定义中提供getinitargs函数,并返回一个tuple,当进行unpickle的时候,python就会自动调用init,并把getinitargs中返回的tuple(*tuple)传递给init

对于new-style对象(继承object),可以提供getnewargs()来提供对象生成时候的参数,这里的生成并不是说python会自动调用初始化函数init,而是在unpickle的时候以Class.new(Class, *arg)的方式创建对象,不过我没有发现对于init函数的调用而且getnewargs只对pickle version2有效,事实上这个只在new需要根据参数定制内存使用的时候才有用。

如果要自定义pickle的数据(zodb中就定义了哪些变量是需要pickle的,哪些不需要),就需要定义getstate函数,这个函数通常是返回一个可以pickle的dict(如果需要处理特殊的对象,就需要有setstate进行对应)。

在默认的情况下是保存dict里的变量,而unpickle的时候也是通过直接更新dict来恢复对象的状态(事实上可以把pickle和unpickle的处理过程看成是递规进行的,而getstatesetstate就是两个处理的钩子)

5、扩展类型的pickle
对于扩展的类型,可以通过实现reduce函数来控制pickle的数据,pickle对这个接口进行无参调用,如果它返回一个字符串,那么pickle在全局名空间里搜索对应名字的对象进行pickle,如果返回的是一个tuple(callable object(对象构造,class 或 具有safe_for_unpickling属性的可调用对象),构造参数,setstate的参数/dict, 参数for list, 参数for dict )reduce_exreduce的扩展版本,它有一个关于pickle的版本参数。另外可以使用copy_reg来注册reduction function, 它接受需要pickle的对象作为参数

6、pickle外部对象(引用对象)
为了”分布式”pickle外部引用对象,pickle提供了“persistent id”机制,简单地说”persistent id”机制就是在pickler上设置persistnet_id属性,它是一个接受将要被pickle的对象作为参数,在这个函数中如果返回str(也就是一个id),那么这个字符串就会被pickle,同时它会被标记为一个“persistent id”,如果返回None,那么这个对象就按照正常的流程被pickle。对称地,在unpickle的时候,需要在unpickler上设置persistent_load属性,它是一个接受“persistent id”的函数,它必须根据这个persistent id返回一个对象,即使这个对象和原来的对象已经不一样了。

如果persistent_load是list对象(或类list对象),那么persistent_id将自动append到这个对象上,这个主要和cpickle中的noload结合使用得到persistent_id,同时又不真正创建对象,以节省内存。在zodb就是这样处理的。

7、子类化Unpickler,自定义unpickle的数据
对于pickle来说,继承Unpickle的类,需要实现load_global()函数,它被调用的时候需要从pickle数据中读取两行数据,第一行是模块名,第二行是类名,同时它需要把得到的数据加入到unpickler的栈中。然后在python的内部处理的时候就会把栈中的信息直接设置到一个空类的class属性,同样的方式是也可以创建一个类的实例,但是并不调用这个类的init函数。而且还必须把load_global()加入到栈中。 而对于cpickle来说,这种定制比较方便,我们只要设置unpickler的find_global属性(callable or None),如果为None,那么
unpickle所有对象的时候都会产生UnpicklingError异常,如果是callable,那么它需要接受模块名和类名参数,并返回对应的类对象(class object)如果在callable处理的过程中抛出异常,那么这个对象将不会被unpickle.
注意:
对于function 和 class 来说, 如果对其进行packle操作,实际上并没有把他们内部的代码或者他们的定义给存储起来,而只是把他们的名称(包括外部模块名)保存了起来。 所以, 当在其他地方unpackle 时,要记得把 对应function或者class的定义导入当前环境才行,否则会提示找不到定义。
这么做的好处在于, 如果你在对某个function或者class 执行packle之后, 对其定义进行了更改,那么当unpackle时,由于只是导出了名字,所以会自动更新为新版本的function 或 class。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值