1. 序列化
序列化是最常见的字节应用.所谓序列化就是将特定对象转化为一串字节序列,用以存储,作为配置文件或者传输.它的反向操作被称为反序列化.
python有丰富的序列化工具如标准库中的base64,json,pickle,又如一些开源的第三方序列化工具如dill,msgpack等. 这些工具虽然都是序列化工具,但设计目的并不相同,本文将会对他们中有代表性的进行介绍.
按功能分,常用于保存信息到硬盘,内存或网络的文件格式有:
- base64 常用于保存文件
- xml 常用于保存结构化信息
而专门针对python的有:
- pickle 用于保存python中的对象
通常作为配置文件格式有这么几种:
xml
常见于那些历史比较久的,尤其是java写的软件,像hadoop就是拿xml作为配置
json
恐怕是js社区最喜欢的配置形式,每个js项目的
package.json
都是其配置ini/conf
也是比较历史悠久的配置形式,常见于C写的项目,python项目中最常见的配置形式
yaml
几乎可以和json划等号的配置形式,似乎是ruby常用的配置形式.很多静态网站项目喜欢使用,github page默认的渲染器
jekyll
就是用的yaml来做配置的
python中json
有标准库json
支持,xml可以使用标准库xml
进行解析.而标准库的ConfigParser
模块可以解析.conf
或者.ini
配置文件..yml
则需要使用第三方包pyyaml
而作为传输信息点载体,常见的有:
xml
Java体系下普遍使用
json
当前最常见的交互载体
messagepack
一种新兴的交互载体,类似json但更快更小,且支持扩展.
base64
通常用于传输字节流
protobuffer
google开发的一种跨语言序列化方式,需要预定义schema,效率极高.
1.1. base64用于序列化文件
Base64是网络上最常见的用于传输8Bit字节代码的编码方式之一,常用于传输小文件,网址等.下面是使用base64序列化网址的例子(注意base64编码的参数只能是bytes)
import base64
url = b"http://blog.hszofficial.site"
url_b64 = base64.b64encode(url)#编码base64
url_b64
b'aHR0cDovL2Jsb2cuaHN6b2ZmaWNpYWwuc2l0ZQ=='
base64.b64decode(url_b64)#解码base64
b'http://blog.hszofficial.site'
很多时候base64被作为加密算法做简单的加密,当然这有点自欺欺人的意思,所谓的加密也只是让人无法直接读出而已
Base64更常见的使用场景之一就是在http协议中传输小图片.它的效果是将整个图片的信息都序列化到一串字节序中
with open("source/mysite.gif","rb") as f: pic = f.read()
pic_64 = base64.b64encode(pic)
pic_64[:5]
url = b"http://blog.hszofficial.site"
0
url = b"http://blog.hszofficial.site"
1
url = b"http://blog.hszofficial.site"
2
url = b"http://blog.hszofficial.site"
3
url = b"http://blog.hszofficial.site"
4
url = b"http://blog.hszofficial.site"
5
url = b"http://blog.hszofficial.site"
6
同学们可以扫描下面的二维码来访问我的主站
url = b"http://blog.hszofficial.site"
7
显然这种方式传输大文件很不靠谱,但小图片还是可以的
1.2. pickle用于持续化python的内置简单对象
有的时候我们想序列化的不光是数据和数据结构,还有对象,这种时候就需要将对象以一定的协议序列化为二进制数据.
python的pickle模块是标准库中最常用的序列化模块,它实现了基本的数据序列和反序列化.通过pickle模块的序列化操作我们能够将程序中运行的对象信息保存到文件中去,永久存储;通过pickle模块的反序列化操作,我们能够从文件中创建上一次程序保存的对象.
需要注意,pickel的文件并不是默认跨版本支持的,可以对照这张表设定需要的参数
pickel到目前为止有5种序列化格式:
版本 | 说明 | 支持python版本 |
---|---|---|
0 | 人类可读的文本,用于最早期 | 全部版本 |
1 | 老的二进制版本文本同样用于早期 | 全部版本 |
2 | 出现于2.3版本,用以支持新类 | 2.3+ |
3 | 出现于3.0版本,用以支持bytes类型 | 3.0+ |
4 | 出现于Python 3.4.用于扩充pickel的支持类型和大对象 | 3.4+ |
python3.5+默认使用的是版本4的pickle格式
要指定使用某一种pickle格式,可以在方法中使用protocol:int=n
来指定
1.2.1. pickle的局限性
pickle的兼容性问题一直让人诟病,除了python没有别的语言使用pickle,而如上表所示,pickle在各个版本的python中也不是默认通用的
pickle实际上并不能传递函数或者类,而是只能记录下它的状态信息而已,因此不能跨模块传递,除此之外,一些对象类型也是不可pickle的.例如Python不能 pickle文件对象(或者任何带有对文件对象引用的对象),因为 Python 在 unpickle 时不能保证它可以重建该文件的状态.
1.2.2. pickle的接口
pickle的接口与json类似带s为序列化或反序列化为字符串,不带的则是处理文件对象
url = b"http://blog.hszofficial.site"
8
url = b"http://blog.hszofficial.site"
9
url_b64 = base64.b64encode(url)#编码base64
0
url_b64 = base64.b64encode(url)#编码base64
1
url_b64 = base64.b64encode(url)#编码base64
2
url_b64 = base64.b64encode(url)#编码base64
3
url_b64 = base64.b64encode(url)#编码base64
4
url_b64 = base64.b64encode(url)#编码base64
5
url_b64 = base64.b64encode(url)#编码base64
6
url_b64 = base64.b64encode(url)#编码base64
4
1.2.3. 命令行工具 pickletools
在python3中提供了一个命令行工具来管理pickle文件
url_b64 = base64.b64encode(url)#编码base64
8
url_b64 = base64.b64encode(url)#编码base64
4
url_b64
0
url_b64
1
可用的参数:
-a, –annotate 用简短的操作码描述来标注每一行。
-o, –output= 写入输出的文件的名称。
-l, –indentlevel= 用于缩进新的MARK级别的空白数。
-m, –memo 拆卸多个物体时,请在拆卸之间保留备注。
-p, –preamble= 当指定多于一个pickle文件时,在每次分解之前打印给定的前导码
1.3. dill用于序列化python对象
dill支持几乎所有的python数据,按github上的说法,它支持:
none, type, bool, int, long, float, complex, str, unicode, tuple, list, dict, file, buffer, builtin, both old and new style classes, instances of old and new style classes, set, frozenset, array, functions, exceptions functions with yields, nested functions, lambdas cell, method, unboundmethod, module, code, methodwrapper, dictproxy, methoddescriptor, getsetdescriptor, memberdescriptor, wrapperdescriptor, xrange, slice, notimplemented, ellipsis, quit
还不支持的有:
frame(帧),generator(生成器对象,因为包含帧状态),traceback(依然是因为无法保存帧状态)
与pickle不同,dill的序列化可以跨模块传递,事实上dill也是为了分布式计算传递python对象而设计的.
dill对python3.5+支持不错,它支持协程,也支持numpy数组,只是序列化的过程中typehint会被消除.
dill的设计目标是为分布式系统传输对象提供支持.因此支持的类型最多,但实际使用它的时候由于它序列化后的字节长度往往过大所以可能反而并不适合用于传输,而它的全面细致让它在用于保存时其实更有优势.
dill在windows上似乎并不是完全支持.因此跨平台使用时需要谨慎.
dill可以直接使用pip安装,使用也相当简单,只要替代pickle就行了,他们接口相同
url_b64
2
url_b64
3
url_b64
4
url_b64
5
url_b64
6
url_b64
7
url_b64
8
1.4. cloudpickle另一个pickle序列化工具
cloudpickle的开发目的也是一样而且现在已经在pyspark和dask中实用(dask中之前使用的是dill),它相较于dill,cloudpickle更加健壮,从目前来看应该是更加适合用于传输的工具.实际上它只是实现了序列化,而反序列化是交给自带的pickle的,这种设计可以在反序列化一端减少依赖,也让反序列化更加快速.
从指标上来说:
- cloudpickle 序列化更加快速,比dill快大约10%
- cloudpickle 反序列化更加快速,比dill块大约85%
- cloudpickle 序列化出的bytes长度大约比dill长20%(带typehints的情况)
- cloudpickle 支持typehits
1.4.1. 序列化
url_b64
9
b'aHR0cDovL2Jsb2cuaHN6b2ZmaWNpYWwuc2l0ZQ=='
0
b'aHR0cDovL2Jsb2cuaHN6b2ZmaWNpYWwuc2l0ZQ=='
1
1.4.2. 反序列化
url = b"http://blog.hszofficial.site"
8
b'aHR0cDovL2Jsb2cuaHN6b2ZmaWNpYWwuc2l0ZQ=='
3
b'aHR0cDovL2Jsb2cuaHN6b2ZmaWNpYWwuc2l0ZQ=='
4
b'aHR0cDovL2Jsb2cuaHN6b2ZmaWNpYWwuc2l0ZQ=='
5
1.5. .ini/.conf文件解析
.ini
文件形如下面
b'aHR0cDovL2Jsb2cuaHN6b2ZmaWNpYWwuc2l0ZQ=='
6
可以分为几个部分:
Sections 节,每个节代表一项配置系列
Case insensitivity 参数字段,具体要配置的字段对应的值
可以理解成一个两层的字典
1.5.1. 写
ConfigParser()
用于初始化一个解析器,这个解析器可以像字典一样使用
b'aHR0cDovL2Jsb2cuaHN6b2ZmaWNpYWwuc2l0ZQ=='
7
1.5.2. 读
初始化一个空的解析器后可以通过.read
方法来读入一个已有的配置.
b'aHR0cDovL2Jsb2cuaHN6b2ZmaWNpYWwuc2l0ZQ=='
8
b'aHR0cDovL2Jsb2cuaHN6b2ZmaWNpYWwuc2l0ZQ=='
9
base64.b64decode(url_b64)#解码base64
0
base64.b64decode(url_b64)#解码base64
1
可以通过sections
方法获取所有节
base64.b64decode(url_b64)#解码base64
2
base64.b64decode(url_b64)#解码base64
3
base64.b64decode(url_b64)#解码base64
4
url = b"http://blog.hszofficial.site"
2
base64.b64decode(url_b64)#解码base64
6
base64.b64decode(url_b64)#解码base64
7
获取到节后,就可以像操作字典一样操作这个节内的内容了
base64.b64decode(url_b64)#解码base64
8
base64.b64decode(url_b64)#解码base64
9
b'http://blog.hszofficial.site'
0
b'http://blog.hszofficial.site'
1
b'http://blog.hszofficial.site'
2
b'http://blog.hszofficial.site'
3
b'http://blog.hszofficial.site'
4
b'http://blog.hszofficial.site'
5
b'http://blog.hszofficial.site'
6
b'http://blog.hszofficial.site'
7
b'http://blog.hszofficial.site'
8
b'http://blog.hszofficial.site'
9
1.6. xml文件解析
xml即可扩展标记语言,它可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言,简单说它是html的爹.大约长这个样子:
with open("source/mysite.gif","rb") as f: pic = f.read()
0
它有如下特征:
它是有标签对组成,
with open("source/mysite.gif","rb") as f: pic = f.read()
1标签可以有属性:
with open("source/mysite.gif","rb") as f: pic = f.read()
2标签对可以嵌入数据:
with open("source/mysite.gif","rb") as f: pic = f.read()
3标签可以嵌入子标签(具有层级关系):
with open("source/mysite.gif","rb") as f: pic = f.read()
4
和html特点上是差不多的.
对于xml的支持,python提供4种解析方式:DOM,SAX和ElementTree,具体特点归纳如下:
方法 | 对应模块 | 实现方式 | 特点 |
---|---|---|---|
DOM | xml.dom | W3C DOM API 的实现.将XML数据在内存中解析成一个树,通过对树的操作来操作XML. | 高内存占用,但解析成树便于分析 |
SAX | xml.sax | SAX API 的实现.用事件驱动模型,通过在解析XML的过程中触发一个个的事件并调用用户定义的回调函数来处理XML文件. | 低内存占用,局部加载,API不友好 |
ElementTree | xml.etree.ElementTree | 一个轻量级的DOM,具有方便友好的API.代码可用性好速度快,消耗内存少 | 比DOM快,API友好,性能和SAX差不多,也支持文档局部加载 |
with open("source/mysite.gif","rb") as f: pic = f.read()
5
with open("source/mysite.gif","rb") as f: pic = f.read()
6
1.6.1. DOM方法:
在xml.dom
模块中我们一般用xml.dom.minidom
子模块来解析xml,其中parse
用于解析文件,parseString
用于解析字符串
with open("source/mysite.gif","rb") as f: pic = f.read()
7
with open("source/mysite.gif","rb") as f: pic = f.read()
8
with open("source/mysite.gif","rb") as f: pic = f.read()
9
pic_64 = base64.b64encode(pic)
0
pic_64 = base64.b64encode(pic)
1
其中节点类型对应的意义
NodeType | Named Constant |
---|---|
1 | ELEMENT_NODE |
2 | ATTRIBUTE_NODE |
3 | TEXT_NODE |
4 | CDATA_SECTION_NODE |
5 | ENTITY_REFERENCE_NODE |
6 | ENTITY_NODE |
7 | PROCESSING_INSTRUCTION_NODE |
8 | COMMENT_NODE |
9 | DOCUMENT_NODE |
10 | DOCUMENT_TYPE_NODE |
11 | DOCUMENT_FRAGMENT_NODE |
12 | NOTATION_NODE |
pic_64 = base64.b64encode(pic)
2
base64.b64decode(url_b64)#解码base64
7
pic_64 = base64.b64encode(pic)
4
pic_64 = base64.b64encode(pic)
5
再看一个更加简单的例子,我们有一个配置文件plant_catalog.xml
用于配置所有植物对应的价格:
pic_64 = base64.b64encode(pic)
6
让我们来解析它
pic_64 = base64.b64encode(pic)
7
pic_64 = base64.b64encode(pic)
8
1.6.2. SAX方法
SAX方法是事件驱动的,所以第一个就是继承回调类并重载回调函数,这和html.parser
类似
ContentHandler类常用方法介绍:
characters(content)方法
调用时机: 从行开始,遇到标签之前,存在字符,content的值为这些字符串. 从一个标签,遇到下一个标签之前,存在字符,content的值为这些字符串. 从一个标签,遇到行结束符之前,存在字符,content的值为这些字符串. 标签可以是开始标签,也可以是结束标签.
startDocument()方法
文档启动的时候调用.
endDocument()方法
解析器到达文档结尾时调用.
startElement(name, attrs)方法
遇到XML开始标签时调用,name是标签的名字,attrs是标签的属性值字典.
endElement(name)方法
遇到XML结束标签时调用.
startElementNS(name, qname, attrs)方法
遇到XML命名空间开始时调用
endElementNS(name, qname)方法
遇到XML命名空间结束时调用
之后只要创建解析器对象就可以像解析html一样解析xml了,我们依然用上面的例子
pic_64 = base64.b64encode(pic)
9
pic_64[:5]
0
pic_64[:5]
1
1.6.3. ElementTree方法
从总结上来看可以说ElementTree方法是最好的方法,Dom处理大文本的时候会相当吃内存,而SAX无法全面解析文档结构.ElementTree怎两者兼而有之,加上友好的api.这也是我最推荐的方法.
将 XML 解析为树的形式:
XML 是一种分级的数据形式,所以最自然的表示方法是将它表示为一棵树.ET有两个对象来实现这个目的
ElementTree
将整个 XML 解析为一棵树
Element
将单个结点解析为树
如果是整个文档级别的操作(比如说读,写,找到一些有趣的元素)通常用ElementTree.单个 XML 元素和它的子元素通常用 Element.
xml.etree.ElementTree
提供接口parse
对文件进行解析,也可以使用类xml.etree.ElementTree.ElementTree
通过指定file关键字指定文件进行解析
如果想要解析文本,则可以使用接口fromstring
来进行解析
pic_64[:5]
2
pic_64[:5]
3
pic_64[:5]
4
pic_64[:5]
5
pic_64[:5]
6
pic_64[:5]
7
pic_64[:5]
8
pic_64[:5]
9
url = b"http://blog.hszofficial.site"
00
url = b"http://blog.hszofficial.site"
01
url = b"http://blog.hszofficial.site"
02
用XPath查找元素
XPath
是一门在 XML 文档中查找信息的语言.XPath 可用来在 XML 文档中对元素和属性进行遍历. XPath 是 W3C XSLT 标准的主要元素,对XPath 的理解是很多高级 XML 应用的基础.ElementTree支持XPath语法查找元素.
什么是 XPath?
XPath可以说是使用路径表达式在 XML 文档中进行导航的一个标准
支持的路径表达式有:
表达式 | 描述 |
---|---|
tag | 选取此节点的所有子节点。 |
/ | 从根节点选取。(绝对路径) |
// | 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。(相对路径) |
. | 选取当前节点。 |
.. | 选取当前节点的父节点。 |
@ | 选取属性。 |
@ xxx=aaa | 选取属性值为aaa的属性 |
* | 匹配任意元素节点 |
@* | 匹配任意元素 |
node() | 匹配任意类型节点 |
[] | 筛选 |
竖条 | 和 |
iterfind()
方法可以使用XPath来查找需要的元素.
我们来试着打印出所有植物和他的对应价格并为大于5美元的计数
url = b"http://blog.hszofficial.site"
03
url = b"http://blog.hszofficial.site"
04
iterparse(source, events=None, parser=None)处理XML流
我们刚讲过如何使用 ET 来将 XML 读入内存并且处理.但它就不会碰到和 DOM 一样的内存问题么?当然会.这也是为什么这个包提供一个特殊的工具,用来处理大型文档,并且解决了内存问题,这个工具叫 iterparse.
他有4个关键字:
- start---元素开始
- end---元素结束
- start-ns---命名空间开始
- end-ns---命名空间结束
iterparse每次返回一个(event, elem)对,可以用这些返回和关键字做匹配,调用回调函数达到解析的目的,还是上面的例子
url = b"http://blog.hszofficial.site"
05
url = b"http://blog.hszofficial.site"
06
1.6.4. 建立XML文档
ElementTree 对象提供了 write 方法可以用来建立xml文档,另外俩虽然也可以,但没这个方便.
我们要建立一个xml,需要从元素入手
url = b"http://blog.hszofficial.site"
07
url = b"http://blog.hszofficial.site"
08
url = b"http://blog.hszofficial.site"
09
url = b"http://blog.hszofficial.site"
10
顺道一提,因为html是xml的子集所以理论上也可以当xml一样解析.
xml是一种比较老的格式化文本协议,至今流行于java项目中,常作为配置文件的格式,像hadoop,spark都还是使用的xml来做配置文件的格式. 在早期,xml也作为网络通信或者对象描述序列化时常常使用的文本协议,但是因为过重现在已经基本被json取代.因此我将它放在这里顺带一提.个人认为xml即便是作为配置文件也过于重了,很难想象除了java社区因为思维惯性还用它以为其他人还用它.
1.7. json用于消息传输
json是当今网络数据传递的标准格式之一,相比较于xml,它更轻量,因此更加便于传输,而且其本身就可以被javascript识别为js对象,更加便于前端处理.
python的标准变量类型与js十分相像因此有天然的Json支持,也就是他的json模块了.
python标准库中有json模块专门用于序列化和反序列化json
与其他序列化不太一样的地方在于,python中json序列化出来并不是bytes而是字符串.其实json作为一种标准格式其实应该和html,xml这些放在一起类比才对,但这里作为序列化工具主要是因为它可以直接对应python中的list和dict
url = b"http://blog.hszofficial.site"
11
url = b"http://blog.hszofficial.site"
12
url = b"http://blog.hszofficial.site"
13
url = b"http://blog.hszofficial.site"
14
url = b"http://blog.hszofficial.site"
15
url = b"http://blog.hszofficial.site"
16
json模块可以直接处理json格式的文件,使用的接口与处理json格式字符串类似,只是方法名后面没有s
url = b"http://blog.hszofficial.site"
17
url = b"http://blog.hszofficial.site"
18
url = b"http://blog.hszofficial.site"
19
1.7.1. ujson
虽然Python自带这个json序列化工具,但因为它的代码是纯净的python代码,因此比较慢,如果想有更快的序列化和反序列化速度的话,可以使用ujson,这个快很多因为是C写的底层.
他们接口完全相同
url = b"http://blog.hszofficial.site"
20
url = b"http://blog.hszofficial.site"
21
url = b"http://blog.hszofficial.site"
22
url = b"http://blog.hszofficial.site"
23
url = b"http://blog.hszofficial.site"
24
url = b"http://blog.hszofficial.site"
25
url = b"http://blog.hszofficial.site"
26
url = b"http://blog.hszofficial.site"
27
url = b"http://blog.hszofficial.site"
28
可以看到ujson无论是序列化还是反序列化都比自带的json模块快上一个数量级
1.8. .yml文件解析
.yml
文件需要使用第三方包pyyaml
来解析,据说效率很高.
.yml
文件形如:
url = b"http://blog.hszofficial.site"
29
yml是一种与json等价的格式,很多现代项目的标准配置格式.比如gitpage默认的静态页面生成框架jekyll
就是使用这种格式配置项目
1.8.1. 写
pyyml可以像json一样直接将字典写成配置文件,甚至可以将pyhton对象写成配置文件
url = b"http://blog.hszofficial.site"
30
url = b"http://blog.hszofficial.site"
31
url = b"http://blog.hszofficial.site"
32
url = b"http://blog.hszofficial.site"
33
url = b"http://blog.hszofficial.site"
34
url = b"http://blog.hszofficial.site"
35
url = b"http://blog.hszofficial.site"
36
url = b"http://blog.hszofficial.site"
37
url = b"http://blog.hszofficial.site"
38
url = b"http://blog.hszofficial.site"
39
1.8.2. 读
url = b"http://blog.hszofficial.site"
40
url = b"http://blog.hszofficial.site"
41
url = b"http://blog.hszofficial.site"
33
1.9. msgpack用于更好的传递消息
msgpack
是现在最快最小的通用序列化协议,官方的说法是It's like JSON.but fast and small.
有测试确实效率非常高.
但其实msgpack和json适用范围并不完全重叠,json作为通用传输协议活跃于web应用,它传递的是字符串,而mspack传递的更多的是bytes,用途也更加底层,常用于分布式服务的消息传递,rpc等
url = b"http://blog.hszofficial.site"
43
1.10. 序列化为bytes
msgpack可以序列化list和dict
url = b"http://blog.hszofficial.site"
44
url = b"http://blog.hszofficial.site"
45
url = b"http://blog.hszofficial.site"
46
url = b"http://blog.hszofficial.site"
47
url = b"http://blog.hszofficial.site"
48
url = b"http://blog.hszofficial.site"
49
url = b"http://blog.hszofficial.site"
50
url = b"http://blog.hszofficial.site"
51
url = b"http://blog.hszofficial.site"
45
url = b"http://blog.hszofficial.site"
53
url = b"http://blog.hszofficial.site"
47
url = b"http://blog.hszofficial.site"
55
1.10.1. 反序列化为字符串
url = b"http://blog.hszofficial.site"
56
url = b"http://blog.hszofficial.site"
45
url = b"http://blog.hszofficial.site"
58
url = b"http://blog.hszofficial.site"
47
url = b"http://blog.hszofficial.site"
60
url = b"http://blog.hszofficial.site"
61
url = b"http://blog.hszofficial.site"
62
1.10.2. 序列化流
unpacker可以反序列化流,它可以从一个流(或从通过其feed方法提供的字节)中分离多个对象.
url = b"http://blog.hszofficial.site"
63
url = b"http://blog.hszofficial.site"
64
1.10.3. 自定义对象序列化
也可以序列化自定义数据类型.以下是datetime.datetime的示例.
我们可以用default
来指定自定义序列化和反序列化的方法.
需要注意的是序列化后的对象字段和值都会被变成bytes,因此需要适当的decode
url = b"http://blog.hszofficial.site"
65
url = b"http://blog.hszofficial.site"
66
url = b"http://blog.hszofficial.site"
67
url = b"http://blog.hszofficial.site"
68
url = b"http://blog.hszofficial.site"
69
url = b"http://blog.hszofficial.site"
70
url = b"http://blog.hszofficial.site"
71
url = b"http://blog.hszofficial.site"
72
如上面的自定义方法,序列化是将容器中的对象找出来,一个一个执行自定义的序列化方法,因此需要用isinstance
方法来判别出类型来再做特殊处理. 而反序列化则是需要通过指定的字段找出序列化的内容,再执行反序列化
1.10.4. 结合dill传递对象
url = b"http://blog.hszofficial.site"
73
url = b"http://blog.hszofficial.site"
74
url = b"http://blog.hszofficial.site"
75
url = b"http://blog.hszofficial.site"
76
url = b"http://blog.hszofficial.site"
68
url = b"http://blog.hszofficial.site"
78
url = b"http://blog.hszofficial.site"
79
url = b"http://blog.hszofficial.site"
71
url = b"http://blog.hszofficial.site"
81
url = b"http://blog.hszofficial.site"
82
url = b"http://blog.hszofficial.site"
83
url = b"http://blog.hszofficial.site"
84
url = b"http://blog.hszofficial.site"
85
1.11. protobuf
protobuf
是目前最快最小的序列化协议,由google开发推动,已经广泛应用于游戏开发,深度学习甚至嵌入式领域.
和上面几乎所有的序列化方式的不同,protobuf
需要先定义数据的格式(schema),然后针对定义好格式的数据实例进行序列化和反序列化.这点上可以看做和pickel
有一些类似(pickel的数据格式其实可以看做是右.py文件定义的).
在使用protobuf
之前你需要几个工具:
proto
语言的编译器protoc
去下载页下载最新的然后在环境变量
Path
中添加protoc
所在的文件夹即可,注意只需要不同操作系统下的protoc
的预编译版本,不需要某种语言指定的版本.如果是树莓派,可能我们还需要使用源码(下载页的Source Code(tar.gz)
或protobuf-cpp
)编译下,以3.6.1
版本为例:url = b"http://blog.hszofficial.site"
86可能下载不下来,这时候可以翻墙下载或者在本地翻墙下载好了scp传过去.使用这种方式后
protoc
就在/usr/local/
目录下了,如果要修改安装目录,可以使用./configure --prefix=<path>
指定.protobuf
protobuf序列化反序列化的python接口,编译出来的python模块会依赖这个库,使用pip
安装熟悉
proto
语法,目前推荐使用proto3grpcio
* 如果你用protobuf是为了做grpc
,那还需要装这个,grpc我们在后面的篇章中讲各种语言的编译插件* 为了多语言支持.
1.11.1. 使用步骤
以一个例子来说明.
- 需要预先用标记语言
proto
定义需要序列化的数据的格式并放在.proto
文件中.下面是一个例子:
url = b"http://blog.hszofficial.site"
87
url = b"http://blog.hszofficial.site"
88
- 编译我们的proto文件
url = b"http://blog.hszofficial.site"
89
url = b"http://blog.hszofficial.site"
90
url = b"http://blog.hszofficial.site"
91
这样我们就可以得到xxxx_pb2.py
这样的模块了,我们需要使用这个模块做序列化反序列化.导入tutorial_pb2
,其中有我们用关键字message
定义的类AddressBook
,Person
,
我们用AddressBook
作为例子.要序列化首先要有被序列化的对象,这个对象就是前面由tutorial_pb2
提供的这些类. 我们需要使用关键字实例化这些类.
url = b"http://blog.hszofficial.site"
92
url = b"http://blog.hszofficial.site"
93
url = b"http://blog.hszofficial.site"
94
url = b"http://blog.hszofficial.site"
95
3.序列化
使用接口.SerializeToString()
可以将对象序列化为bytes
url = b"http://blog.hszofficial.site"
96
url = b"http://blog.hszofficial.site"
97
- 反序列化
使用接口.ParseFromString()
可以将bytes反序列化为对象
url = b"http://blog.hszofficial.site"
98
url = b"http://blog.hszofficial.site"
95
总体来看protobuf在小范围内使用的话使用起来是比较麻烦的.又要先定义又要编译.但在比较大规模的使用场景下其实它更加高效
- 本身编译快,结果小
- schema即接口,强制梳理业务流程,预先定义好数据使用范围,接口形式等,相当于优化了开发流程
- 所有数据信息都在
.proto
文件中,免去了数据文档和相关的维护,如果是使用grpc那连接口文档都可以省了
它带来的问题就是需要有一个比较好的方式可以妥善管理.proto
文件.
还没有评论,来说两句吧...