Python的序列化与反序列化
Python的序列化与反序列化
作者:尹正杰
版权声明:原创作品,谢绝转载!否则将追究法律责任。
序列化和反序列化可以大致分为两类:文本序列化和二进制序列化。比如python提供的pickle库,他就是基于二进制方式的序列化。而json则是文本方式的序列化,需要遵守相应的文件格式。
由于pickle序列化和发序列化仅仅同版本的Python可以使用(pickle库的序列化在python2,python3也存在不兼容的问题)。因此我们还会学习MessagePack,它是一个基于二进制高效的对象序列化类库,可用于跨语言通信。
一.序列化概述
1>.为什么要序列化
内存中的字典,列表,集合以及各种对象,如何保存到一个文件中?
如果是自己定义的类的实例,如何保存到一个文件中?
如何从文件中读取数据,并让他在内存中再次恢复成自己对应的类的实例?
要设计一套协议,按照某种规则,把内存这种数据保存到文件中。文件是一个字节序列,所以必须把数据转换成字节序列,输出到文件。这就是序列化。
反之,从文件的字节序列恢复到内存并且还要原来的类型,就是反序列化。
2>. 序列化相关术语
serialization
将内存中对象存储下来,把它变成一个个字节。
deserialization
将文件的一个个字节恢复成内存中对象。
序列化保存到文件就是持久化。
可以将数据序列化后持久化,或者网络传输;也可以将从文件中或者网络接受到的字节序列反序列化。Python提供了pickle库,它是一种基于二进制方式的序列化。
3>.序列化应用
一般来说,本地序列化的情况,应用较少。大多数场景都应用在网络传输中。
将数据序列化后通过网络传输到远程节点,远程服务器上的服务将接收的数据反序列化后,就可以使用了。
但是,要注意一点,远程接收端,反序列化时必须有对应的数据类型,否则就会报错。尤其是自定义类,必须远程得有一致的定义。
现在,大多数项目,都不是单机的,也不是单服务的,需要多个程序之间配合。需要通过网络将数据传送到其它节点上去,这就需要大量的序列化,反序列化过程。
但是,问题是Python程序之间可以使用pickle解决序列化,反序列化,,如果是跨平台,跨语言,跨协议pickle就不太适合了,就需要公共的协议。例如XML,JSON,PROTOCOL BUFFER等。
不同的协议,效率不同,学习曲线不同,适用不同场景,要根据不同的情况分析选型。
二.pikcle模块
1>.方法说明
dumps:
对象序列化为bytes对象,一般用于处理网络传输的数据。
dump:
对象序列化到文件对象,比如存入本地文件。
loads:
从bytes对象反序列化,一般用于处理网络接受的数据。
load:
对象发序列化,从文件读取数据。
2>.序列化和反序列化基础数据类型
1 #!/usr/bin/env python 2 #_*_conding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie 5 6 import pickle 7 8 file_name = r"E:\temp\test\ser.txt" 9 10 s1 = 99 11 s2 = 'c' 12 s3 = list("123") 13 s4 = {"a":1,"b":"abc","c":[1,2,3]} 14 15 #序列化 16 with open(file_name,"wb") as f: 17 pickle.dump(s1,f) 18 pickle.dump(s2,f) 19 pickle.dump(s3,f) 20 pickle.dump(s4,f) 21 22 23 #反序列化 24 with open(file_name,"rb") as f: 25 print(f.read(),f.seek(0)) 26 for i in range(4): 27 x = pickle.load(f) 28 print(x,type(x)) 29 30 31 32 #以上代码执行结果如下: 33 b'\x80\x03Kc.\x80\x03X\x01\x00\x00\x00cq\x00.\x80\x03]q\x00(X\x01\x00\x00\x001q\x01X\x01\x00\x00\x002q\x02X\x01\x00\x00\x003q\x03e.\x80\x03}q\x00(X\x01\x00\x00\x00aq\x01K\x01X\x01\x00\x00\x00bq\x02X\x03\x00\x00\x00abcq\x03X\x01\x00\x00\x00cq\x04]q\x05(K\x01K\x02K\x03eu.' 0 34 99 <class 'int'> 35 c <class 'str'> 36 ['1', '2', '3'] <class 'list'> 37 {'a': 1, 'b': 'abc', 'c': [1, 2, 3]} <class 'dict'>
3>.序列化和反序列化对象
1 #!/usr/bin/env python 2 #_*_conding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie 5 6 import pickle 7 8 file_name = r"E:\temp\test\ser.txt" 9 10 #定义对象 11 class Person: 12 def __init__(self,name,age=18): 13 self.__name = name 14 self.__age = age 15 16 @property 17 def name(self): 18 return self.__name 19 20 @name.setter 21 def name(self,name): 22 self.__name = name 23 24 def getage(self): 25 return self.__age 26 27 def setage(self,age): 28 self.__age = age 29 30 age = property(fget=getage,fset=setage,doc="age property") 31 32 def __repr__(self): 33 return "{} 今年已经 {} 岁啦!".format(self.__name,self.__age) 34 35 36 p1 = Person("Yinzhengjie",20) 37 38 p1.age = 27 39 40 #序列化 41 ser = pickle.dumps(p1) 42 print("ser = {}".format(ser)) 43 44 #反序列化 45 p2 = pickle.loads(ser) 46 print(type(p2)) 47 print(p2) 48 49 50 51 #以上代码执行结果如下: 52 ser = b'\x80\x03c__main__\nPerson\nq\x00)\x81q\x01}q\x02(X\r\x00\x00\x00_Person__nameq\x03X\x0b\x00\x00\x00Yinzhengjieq\x04X\x0c\x00\x00\x00_Person__ageq\x05K\x1bub.' 53 <class '__main__.Person'> 54 Yinzhengjie 今年已经 27 岁啦!
三.json模块
1>.Json概述
JSON(JavaScript Object Notation,JS对象标记)是一种轻量级的数据交换格式。它基于ECMAScript(w3c组织制定的JS规范)的一个子集,采用完全独立与编程语言的文本格式来存储和表示数据。
一般来说,json编码的数据很少落地,数据都是通过网络传输,传输的时候,需考虑压缩它。本质上来说它就是个文本,就是个字符串。
json很简单,几乎所有编程都支持Json,所以应用范围十分广泛。
官方网址请参考:http://json.org/。
2>.Json数据类型
值:
双引号引起来的字符串,数值,true和false,null,对象,数组,这些都是值。
字符串:
由双引号包围起来的任意字符的组合,可以有转义字符。
数值:
有正负,有整数,浮点数。
对象:
无序的键值对的集合。
格式:{key1:value1,...keyn:valulen}
key必须是一个字符串,需要双引号包围这个字符串。
value可以是任意合法的值。
数组:
有序的值和集合。
格式:[val1,...valn]
案例如下:
{
"person":[
{
"name":"jason",
"age":18
},
{
"name":"yinzhengjie"
"age":16
}
],
"total":2
}
3>.Python与Json
Python支持少量内建数据类型到Json类型的转换,大致如下:
Python类型 Json类型
True true
False false
None null
str string
int integer
float float
list array
dict object
4>.json模块应用案例
1 #!/usr/bin/env python 2 #_*_conding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie 5 6 import json 7 8 src = {'name':'Jason','age':20,'interest':('music','movie'),'class':['python']} 9 10 11 json_ser = json.dumps(src) #进行json编码 12 print(json_ser,type(json_ser)) #注意引号,括号的变化,数据类型的变化。 13 14 dest = json.loads(json_ser) 15 print(dest) 16 print(id(src),id(dest)) 17 18 19 20 #以上代码执行结果如下: 21 {"name": "Jason", "age": 20, "interest": ["music", "movie"], "class": ["python"]} <class 'str'> 22 {'name': 'Jason', 'age': 20, 'interest': ['music', 'movie'], 'class': ['python']} 23 2114747181640 2114747436952
三.MessagePack模块
1>.MessagePack概述
MessagePack是一个基于二进制高效的对象序列化类库,可用于跨语言通信。
它可以像JSON那样,在许多种语言之间交换结构对象。
但是它比JSON更快速也更轻巧。
支持Python,Ruby,Java,C/C++等众多语言。宣称比Google Protocol Buffers还要快4倍(这话建议大家听听就好,要以实际测试为准)。
兼容json和pickle。
2>.MessagePack安装
messagePack官网地址:https://msgpack.org/ 安装messagePack方式: C:\Users\yinzhengjie>pip install msgpack Collecting msgpack Downloading https://files.pythonhosted.org/packages/41/0a/49b522c5b23006d60669ec8dc97558e91880673e170108e0e9e21fc452e3/msgpack-0.6.2-cp37-cp37m-win_amd64.whl (68kB) 100% |████████████████████████████████| 71kB 112kB/s Installing collected packages: msgpack Successfully installed msgpack-0.6.2 C:\Users\yinzhengjie>
3>. 常用方法
packb序列化对象。提供了dumps来兼容pickle和json。
unpackb反序列化对象。提供了loads来兼容。
pack序列化对象保存到文件对象。提供了dump来兼容。
unpack反序列化对象保存到文件对象。提供红了load来兼容。
上面的内容了解一下即可,咱们生产环境中还是只用dump(s)和load(s)方法来进行序列化和反序列化,没有必要记得太多,反而浪费脑细胞~嘿嘿嘿。
4>.对比pickle,json和messagePack的序列化字节大小
1 #!/usr/bin/env python 2 #_*_conding:utf-8_*_ 3 #@author :yinzhengjie 4 #blog:http://www.cnblogs.com/yinzhengjie 5 6 import pickle 7 import json 8 import msgpack 9 10 11 #导入模块,都是标识符 12 methods = (pickle,json,msgpack) 13 14 src = {'person':[ 15 {'name':'Jason','age':18,'hobby':('movie','music','Mountain climbing')}, 16 {'name':'YinZhengjie','age':20,'hobby':('Basketball','ping-pong','Blogging')} 17 ], 18 'total':2 19 } 20 21 #进行序列化操作 22 for method in methods: 23 s1 = method.dumps(src) 24 if method == json: #我们这里仅对json格式文件做一个简单的压缩处理,但生产环境建议不要这样用,因为这样写逻辑并不严谨。 25 s1 = s1.replace(' ','') 26 print(method.__name__,type(s1),len(s1),s1) 27 28 29 print("*" * 20 + "我是分割线" + "*" * 20) 30 31 #进行反序列话操作 32 # s2 = msgpack.loads(s1,encoding="utf8") #这个方法不推荐使用,使用后会抛出"DeprecationWarning: encoding is deprecated, Use raw=False instead."的警告信息哟~ 33 # print(type(s2),s2) 34 s3 = msgpack.loads(s1,raw=False) #官方推荐使用"raw=False"的方式 35 print(type(s3),s3) 36 37 38 39 40 #以上代码执行结果如下: 41 pickle <class 'bytes'> 225 b'\x80\x03}q\x00(X\x06\x00\x00\x00personq\x01]q\x02(}q\x03(X\x04\x00\x00\x00nameq\x04X\x05\x00\x00\x00Jasonq\x05X\x03\x00\x00\x00ageq\x06K\x12X\x05\x00\x00\x00hobbyq\x07X\x05\x00\x00\x00movieq\x08X\x05\x00\x00\x00musicq\tX\x11\x00\x00\x00Mountain climbingq\n\x87q\x0bu}q\x0c(h\x04X\x0b\x00\x00\x00YinZhengjieq\rh\x06K\x14h\x07X\n\x00\x00\x00Basketballq\x0eX\t\x00\x00\x00ping-pongq\x0fX\x08\x00\x00\x00Bloggingq\x10\x87q\x11ueX\x05\x00\x00\x00totalq\x12K\x02u.' 42 json <class 'str'> 171 {"person":[{"name":"Jason","age":18,"hobby":["movie","music","Mountainclimbing"]},{"name":"YinZhengjie","age":20,"hobby":["Basketball","ping-pong","Blogging"]}],"total":2} 43 msgpack <class 'bytes'> 130 b'\x82\xa6person\x92\x83\xa4name\xa5Jason\xa3age\x12\xa5hobby\x93\xa5movie\xa5music\xb1Mountain climbing\x83\xa4name\xabYinZhengjie\xa3age\x14\xa5hobby\x93\xaaBasketball\xa9ping-pong\xa8Blogging\xa5total\x02' 44 ********************我是分割线******************** 45 <class 'dict'> {'person': [{'name': 'Jason', 'age': 18, 'hobby': ['movie', 'music', 'Mountain climbing']}, {'name': 'YinZhengjie', 'age': 20, 'hobby': ['Basketball', 'ping-pong', 'Blogging']}], 'total': 2}
5>.总结
MessagePack简单易用,高效压缩,支持语言丰富。所以,用它序列化也是一种很好的选择。Python很大大名鼎鼎的库都是用了msgpack。
上例中,之所以pickle比json序列化的结果还要大,原因主要是pickle要解决所有python数据类型的序列化,要记录各种数据类型包括自定义的类。而json只需要支持少数几种类型,所以就可以很简单,都不需要类型的描述字符。但大多数情况下,我们序列化的数据都是这些简单的类型。
本文来自博客园,作者:尹正杰,转载请注明原文链接:https://www.cnblogs.com/yinzhengjie/p/11149063.html,个人微信: "JasonYin2020"(添加时请备注来源及意图备注,有偿付费)
当你的才华还撑不起你的野心的时候,你就应该静下心来学习。当你的能力还驾驭不了你的目标的时候,你就应该沉下心来历练。问问自己,想要怎样的人生。