【Python】 python对象的文件化 pickle
pickle
之前隐隐约约在哪里看到过pickle这个模块但一直没怎么用过。然后让我下定决心学习一下这个模块的原因竟然是【妹抖龙女(男)主在工作中用到了pickle哈哈哈】。嗯嗯,不扯皮了。pickle的作用是把Python的对象序列化为适合储存到文件、可通过网络传输、存放于数据库中的字节流方式。相当于把原先只存在于内存中的python对象给固化到硬盘上来。这个和之前用到过的shelve有点像,不过shelve只支持字典一个格式,而pickle应该是百通的。
关于对象序列化(or you will “文件化”)的一些理论性的东西可以参见http://www.cnblogs.com/cobbliu/archive/2012/09/04/2670178.html。大体来说,序列化分成几种格式,比如文本格式,二进制格式等等。不同的序列化格式会有不同的存储性能和运行效率等,但从固化对象这个角度来讲各种格式都可以做到。
python还有一个cPickle模块,作用基本上和pickle相同,但是具有更好的性能。在能用cPickle的时候最好用它,比如像小林一样:
try:
import cPickle as pickle
except ImportError:
import pickle
pickle模块可以处理的对象类型有None, 数字和字符串, 只包含可序列化对象的元组,列表,字典,以及用户自定义的类的实例等。
pickle模块的format_version成员变量可以查看当前pickle的格式版本,而compatible_formats变量可以显示当前pickle兼容哪些之前的版本。
■ 基本用法
pickle的基本用法很简单,基本上就是四个方法,dumps,dump 以及 loads,load。
dumps(object[,protocol]) 把一个对象序列化,返回一个包含序列化信息的字符串
dump(object,file[,protocol]) 把一个对象序列化后存储到文件file中。
以上两者的protocol参数用来指定数据的输出格式,0是默认值,指文本格式且兼容之前的所有python版本,1指二进制格式且向前兼容所有python版本;2,3也各有所指,不详述了。当object不支持序列化时会在调用dump时引发pickle.PicklingError异常。
loads(string) 从一个包含序列化信息的字符串中解析出一个对象并返回
load(file) 从一个文件中加载解析出一个对象并返回
以上两个方法无需指定协议,方法会自动判断当前序列化内容的协议。如果文件含有无法解码的数据(原因包括格式不对,数据遭到损坏等等可能)则会引发pickle.UnpicklingError异常。另外在load方法中如果检测到文件的结尾则会引发EOFError异常,可以利用这个异常来判断循环遍历load时结束的时机。
■ 更多细节
● 关于在序列化存入文件时的dump顺序和load顺序的关系:
当把多个对象dump到一个文件里,再load的时候会发生什么?其实,pickle在load的时候会自动识别文件中对象之间的分隔然后一个个地load出来。比如我先后把A和B pickle.dump进一个文件中,然后再从这个文件中先后pickle.load出C和D。这些对象间的对应关系就是A和C相等,B和D相等。
● 上面提到了关于不支持序列化的对象。比如文件对象就是一个不支持序列化的对象。想想也是啊,把文件对象固化到另一个文件中,确实没什么必要。。
● 经过pickle的dump再load出来的对象,在内容上和原对象相等,但是并不是同一个对象。新出来的对象应该是原对象的一个副本。比如把A dump进一个文件然后load出B。此时A==B是True但是A is B是False。并且,每一次pickle.load其实际上是从文件中load出一个值,然后创建一个新对象来引用这个值,所以无论之前dump进文件的对象的引用情况是怎么样的,load出来的对象之间都是互相独立的:
a = [1,2,3]
b = a
print a is b #True
fi = open("file.pk","w")
pickle.dump(a,fi)
pickle.dump(b,fi)
fi.close() fi = open("file.pk","r")
c = pickle.load(fi)
d = pickle.load(fi)
fi.close()
print c is d #False
如果想要让c和d也都是引用同一个内容的话,那就需要在dump的时候就把这种关系确定下来。也就是说要把两个对象引用的内容是相同的这个情况用“值”的形式也固化到文件中去,一个常见的做法就是把原先分两个dump换成dump一个元组:
a = [1,2,3]
b = a
##文件开关都省略了##
pickle.dump((a,b),fi) c,d = pickle.load(fi) print c is d #True
这样做的话就可以使得dump操作把这种引用关系也dump进文件里面了。但是这样做并不好,一点都不好懂搞一个元组出来有什么意义。所以pickle还提供了Pickler和Unpickler两个类。这两个类可以在还原的时候还原出原来的对象之间的关系。构造方法是Pickler(file[,protocol])。比如:
a = [1,2,3]
b = a
fi = open("file","w")
pickler = pickle.Pickler(fi)
pickler.dump(a)
pickler.dump(b)
fi.close() fi = open("file","r")
unpickler = pickle.Unpickler(fi)
c = unpickler.load()
d = unpickler.load()
print c is d #True
fi.close()
究其原理,其实是Pickler类里面有一个字典,会记住每一个dump进来的对象的id(而不仅仅是它的值),当接下来dump进来的对象有相同的id时它就为这个对象在文件中创建一个引用就好了而不是一个有完整内容的副本。这样的一个文件,在load的时候自然也就会保留出两个原来的对象引用同一片内容的特点了。注意:用Pickler类dump出来的文件一定要用Unpickler类来解析,普通的pickle.load可能会出现错误。
Pickler类中还有一个pickler.clear_memo()方法,就是用来清空Pickler类中的那个字典,使得新dump进来的对象即使拥有和之前dump进的对象相同的引用也会创建一个新的副本。
● 关于自定义类实例的pickle
pickle模块可以处理的对象类型有None, 数字和字符串, 只包含可序列化对象的元组,列表,字典,以及用户自定义的类的实例等。在处理自定义类的实例的时候要特别注意,对于这种实例的序列化,pickle基本上只会记录这个实例各种属性的值(通常是__dict__属性中的),而不记录类和成员方法的代码。所以当在反序列化(或者把这个行为称之为重建实例)实例的时候,如果在当前的Unpickler所在环境下找不到相关的类或方法的定义,或者相关定义不能满足现在这个实例的一些操作的时候是会报错的。另外需要注意的是,重建实例时并不会调用这个实例所属类的__init__方法,所以“重建”并不是从零开始的重建,而是把记录在文件里的那些数据填入到现有的一个框架里面的过程。比如两个放在同一目录下的脚本,第一个脚本:
class Test(object):
def __init__(self,msg):
self.msg = msg def printmymessage(self):
print self.msg test = Test("Hello")
test.msg = "konnnitiha"
fi = open("test.pk","w")
pickler = pickle.Pickler(fi)
pickler.dump(test)
fi.close()
第二个脚本:
class Test(object):
def __init__(self,msg):
self.msg = msg def printmymessage(self):
print "new class "+self.msg
fi = open("test.pk","r")
u = pickle.Unpickler(fi)
myins = u.load()
myins.printmymessage() #打印出来的是new class konnnitiha
fi.close()
这样的一个输出说明了两个问题。1.被dump进文件的对象是带有其最新的属性值的,所以输出的是konnitiha而不是Hello。2.重建实例的时候用的类和方法的定义是看Unpickler所在环境决定的,比如这里的打印信息有new class,反过来说,如果脚本2里没有定义Test类或者printmymessage方法的话就会报错了。
另外要注意,要dump的类的类定义必须出现在模块的最顶层,这意味着它们不能是嵌套的类(在其它类或函数中定义的类)。
● 再论自定义类实例的pickle,__getstate__和__setstate__方法
首先要说的是类的特殊方法,__getstate__和__setstate__。pickle一个类的实例时其实是把这个类的__getstate__方法返回的值存了起来,在默认情况下这个方法返回的就是类的__dict__。而unpickle就是把从文件解析出来的东西传递给__setstate__方法,让这个方法做一些处理(默认就是把得到的东西传给新实例的__dict__),这相当于是重建的过程。
通过对一个类的这两个特殊方法的重载,可以灵活地控制经过pickle和unpickle后实例,使得前后两个实例带有的数据可以不一样(在默认情况下两者肯定是完全相同的)。比如在getstate里面只返回一部分的属性等。下面就是一个例子:(https://www.oschina.net/question/253614_115412)
class Slate:
'''存储一个字符串和一个变更log,当Pickle时会忘记它的值''' def __init__(self, value):
self.value = value
self.last_change = time.asctime()
self.history = {} def change(self, new_value):
# 改变值,提交最后的值到历史记录
self.history[self.last_change] = self.value
self.value = new_value
self.last_change = time.asctime() def print_changes(self):
print 'Changelog for Slate object:'
for k, v in self.history.items():
print '%st %s' % (k, v) def __getstate__(self):
# 故意不返回self.value 或 self.last_change.
# 当unpickle,我们希望有一块空白的"slate"
return self.history def __setstate__(self, state):
# 让 self.history = state 和 last_change 和 value被定义
self.history = state
self.value, self.last_change = None, None
这个例子可以实现每次把Slate类的实例pickle到磁盘上时,让pickle只记录它的修改历史记录而不记录当前值和最后修改时间,当在另一个地方unpickle它,重建实例的时候根据__setstate__方法的指定,历史记录被写进新的实例中,但是值和最后修改时间被指定为None也就是空白状态。
● 通过pickle得到的序列化数据是python专用的,只有python才能解析,并不像xml,json这种数据格式可以通用到很多体系中。
【Python】 python对象的文件化 pickle的更多相关文章
- python 列表 字典 读写文件:pickle模块的基本使用
python数据持久存储:pickle模块的基本使用(转载) 作者: pzxbc 出处: http://pzxbc.cnblogs.com/ 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保 ...
- python将对象写入文件,以及从文件中读取对象
原文地址: http://www.voidcn.com/article/p-fqtqpwxp-wo.html 写入文件代码: >>> import sys, shelve >& ...
- python基础——元组、文件及其它
Python核心数据类型--元组 元组对象(tuple)是序列,它具有不可改变性,和字符串类似.从语法上讲,它们便在圆括号中,它们支持任意类型.任意嵌套及常见的序列操作. 任意对象的有序集合:与字符串 ...
- python pickle模块的使用/将python数据对象序列化保存到文件中
# Python 使用pickle/cPickle模块进行数据的序列化 """Python序列化的概念很简单.内存里面有一个数据结构, 你希望将它保存下来,重用,或者发送 ...
- Python之对象的永久保存模块---pickle
经常遇到在Python程序运行中得到了一些字符串.列表.字典等数据,想要长久的保存下来,方便以后使用,而不是简单的放入内存中关机断电就丢失数据. 这个时候Pickle模块就派上用场了,它可以将对象转换 ...
- python笔记-7(shutil/json/pickle/shelve/xml/configparser/hashlib模块)
一.shutil模块--高级的文件.文件夹.压缩包处理模块 1.通过句柄复制内容 shutil.copyfileobj(f1,f2)对文件的复制(通过句柄fdst/fsrc复制文件内容) 源码: Le ...
- python学习笔记(7)文件的访问与函数式编程
一.文件读写的3中方法 1.直接读入 fiel1=open('test.txt') file2=open('output.txt') while True: line=file1.readLine() ...
- Python学习笔记005_文件_OS_模块_pickle
>>> >>> # 文件 open()方法是打开文件,它有很多参数,第一个文件名是必须的(带路径)>>> >>> f = ope ...
- Python第十四天 序列化 pickle模块 cPickle模块 JSON模块 API的两种格式
Python第十四天 序列化 pickle模块 cPickle模块 JSON模块 API的两种格式 目录 Pycharm使用技巧(转载) Python第一天 安装 shell 文件 Py ...
随机推荐
- 问题解决了,可是为什么呢?could not find the main class.program will exitmain
今天重新学习socket编写简单的在线聊天,简单功能实现的情况下,一时心血来潮便想要把这程序打成可执行的jar包,以便于在桌面直接双击运行. 参照自己之前写的那篇<>打好两个jar包以后却 ...
- Windows PE入门基础知识:Windows PE的作用、命名规则、启动方式、启动原理
Windows PE的全名是WindowsPreinstallationEnvironment(WinPE)直接从字面上翻译就 是"Windows预安装环境".微软的本意是:Win ...
- 程序员的职场潜意识Top10
什么叫潜规则?其实就是不明文规定的一些规则,关键是他没法明文规定,因为有的规则太没节操.在我们职场中有些规则你不遵守将举步艰难,而要玩转这些潜规则,那么你必须要具备如下10个潜意识: 1.项目会议.那 ...
- Java中的List转换成JSON报错(四)
1.错误描述 Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/colle ...
- html中的块元素和内联元素的区别
一.定义 块元素一般都从新行开始,它可以容纳内联元素和其他块元素,可设置高度.宽度和边距等. 内联元素一般都是基于语义级的基本元素,它只能容纳文本或其他内联元素,主要特点是:和其他元素位于同一行上,高 ...
- Android查缺补漏(线程篇)-- AsyncTask的使用及原理详细分析
本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8515304.html 一.AsyncTask的使用 AsyncTask是一种轻 ...
- 浅谈Javascript中的Label语句
如: begin: for (var i = 0; i < 10 ; i++ ){ alert(i); } 举一个比较典型的例子,看完后即明白 Label 的应用:(未添加 Label) var ...
- 第三篇:一个Spark推荐系统引擎的实现
前言 经过2节对MovieLens数据集的学习,想必读者对MovieLens数据集认识的不错了:同时也顺带回顾了些Spark编程技巧,Python数据分析技巧. 本节将是让人兴奋的一节,它将实现一个基 ...
- HDU 3416 Marriage Match IV(最短路,网络流)
题面 Do not sincere non-interference. Like that show, now starvae also take part in a show, but it tak ...
- OpenAI dota2大战人类顶尖选手视频
AI大战Dendi:http://www.bilibili.com/video/av13267474/?zw#quality=3 AI大战Sumail:http://www.bilibili.com/ ...