在Python中应用protobuf

本次我们来聊一聊protobuf,它是一个数据序列化和反序列化协议,因此它和json的定位是一样的。当客户端需要传递数据给服务端时,会将内存中的对象序列化成一个可以在网络中传输的二进制流,服务端收到之后再反序列化,得到内存中的对象。

不过既然都有json了,还会出现protobuf,那就说明protobuf相较于json有着很大的优势。来看一下优缺点:

总结一下,protobuf全称为ProtocolBuffer,它是Google开发的一种轻量并且高效的结构化数据存储格式,性能要远远优于json和xml。另外protobuf经历了两个版本,分别是protobuf2和protobuf3,目前主流的版本是3,因为更加易用。

下面就来开始学习protobuf吧。

但是别忘记安装,直接pip3installgrpciogrpcio-toolsprotobuf即可

编写一个简单的protobuf文件

protobuf文件有自己的语法格式,所以相比json它的门槛要高一些。我们创建一个文件,文件名为girl.proto。

protobuf文件的后缀是.proto

//syntax负责指定使用哪一种protobuf服务//注意:syntax必须写在非注释的第一行syntax="proto3";//包名,这个目前不是很重要,你删掉也是无所谓的packagegirl;//把UserInfo当成Python中的类//name和age当成绑定在实例上的两个属性messageUserInfo{stringname=1;//=1表示第1个参数int32age=2;}protobuf文件编写完成,然后我们要用它生成相应的Python文件,命令如下:

我们要用protobuf文件生成Python文件,所以--python_out负责指定Python文件的输出路径,这里是当前目录;-I表示从哪里寻找protobuf文件,这里也是当前目录;最后的girl.proto就是指定的protobuf文件了。

我们执行该命令,会发现执行完之后多了一个girl_pb2.py,我们直接用即可。注意:这是基于protobuf自动生成的Python文件,我们不要修改它。如果参数或返回值需要改变,那么应该修改protobuf文件,然后重新生成Python文件。

然后我们来看看采用protobuf协议序列化之后的结果是什么,不是说它比较高效吗?那么怎能不看看它序列化之后的结果呢,以及它和json又有什么不一样呢?

然后还有一个关键地方的就是,json这种数据结构比较松散。你在返回json的时候,需要告诉调用你接口的人,返回的json里面都包含哪些字段,以及类型是什么。但protobuf则不需要,因为字段有哪些、以及相应的类型,都必须在文件里面定义好。别人只要拿到.proto文件,就知道你要返回什么样的数据了,一目了然。

在服务端之间传输protobuf

如果两个服务需要彼此访问,那么最简单的方式就是暴露一个HTTP接口,服务之间发送HTTP请求即可彼此访问,至于请求数据和响应数据,则使用JSON。

所以通过HTTP+JSON是最简单的方式,也是业界使用最多的方式。但这种方式的性能不够好,如果是同一个内网的多个服务,那么更推荐使用gRPC+protobuf。关于gRPC以后再聊,我们来看看protobuf数据在HTTP请求中是如何传递的。

首先还是编写.proto文件。

//文件名:girl.protosyntax="proto3";packagegirl;messageRequest{stringname=1;int32age=2;}messageResponse{stringinfo=1;}一个protobuf文件中可以定义任意个message,在生成Python文件之后每个message会对应一个同名的类。然后我们执行之前的命令,生成Python文件。

接下来使用Tornado编写一个服务:

fromabcimportABCfromtornadoimportweb,ioloopimportgirl_pb2classGetInfoHandler(web.RequestHandler,ABC):asyncdefpost(self):#拿到客户端传递的字节流#这个字节流应该是由girl_pb2.Request()序列化得到的content=self.request.body#下面进行反序列化request=girl_pb2.Request()request.ParseFromString(content)#获取里面的name和age字段的值name=request.nameage=request.age#生成Response对象response=girl_pb2.Response(info=f"name:{name},age:{age}")#但Response对象不能直接返回,需要序列化returnawaitself.finish(response.SerializeToString())app=web.Application([("/get_info",GetInfoHandler)])app.listen(9000)ioloop.IOLoop.current().start()整个过程很简单,和JSON是一样的。然后我们来访问一下:

protobuf的基础数据类型

在不涉及gRPC的时候,protobuf文件是非常简单的,你需要返回啥结构,那么直接在.proto文件里面使用标识符message定义即可。

message消息名称{类型字段名=1;类型字段名=2;类型字段名=3;}但是类型我们需要说一下,之前用到了两个基础类型,分别是string和int32,那么除了这两个还有哪些类型呢?

以上是基础类型,当然还有复合类型,我们一会单独说,先来演示一下基础类型。编写.proto文件:

//文件名:basic_type.protosyntax="proto3";packagebasic_type;messageBasicType{//字段的名称可以和类型名称一致,这里为了清晰//我们就直接将类型的名称用作字段名int32int32=1;sint32sint32=2;uint32uint32=3;fixed32fixed32=4;sfixed32sfixed32=5;int64int64=6;sint64sint64=7;uint64uint64=8;fixed64fixed64=9;sfixed64sfixed64=10;doubledouble=11;floatfloat=12;boolbool=13;stringstring=14;bytesbytes=15;}然后我们来生成Python文件,命令如下:

python3-mgrpc_tools.protoc--python_out=.-I=.basic_type.proto

执行之后,会生成basic_type_pb2.py文件,我们测试一下:

repeat和map

repeat和map是一种复合类型,可以把它们当成Python的列表和字典。

//文件名:girl.protosyntax="proto3";packagegirl;messageUserInfo{//对于Python而言//repeated表示hobby字段的类型是列表//string则表示列表里面的元素必须都是字符串repeatedstringhobby=1;//map表示info字段的类型是字典//字典的键值对必须都是字符串mapinfo=2;}我们执行命令,生成Python文件,然后导入测试一下。

message的嵌套

通过标识符message即可定义一个消息体,大括号里面的则是参数,但参数的类型也可以是另一个message。换句话说,message是可以嵌套的。

//文件名:girl.protosyntax="proto3";packagegirl;messageUserInfo{repeatedstringhobby=1;//BasicInfo定义在外面也是可以的messageBasicInfo{stringname=1;int32age=2;stringaddress=3;}BasicInfobasic_info=2;}生成Python文件,导入测试一下。

枚举类型

再来聊一聊枚举类型,它通过enum标识符定义。

//里面定义了两个成员,分别是MALE和FEMALEenumGender{MALE=0;FEMALE=1;}这里需要说明的是,对于枚举来说,等号后面的值表示成员的值。比如一个字段的类型是Gender,那么在给该字段赋值的时候,要么传0要么传1。因为枚举Gender里面只有两个成员,分别代表0和1。

而我们前面使用message定义消息体的时候,每个字段后面跟着的值则代表序号,从1开始,依次递增。至于为什么要有这个序号,是因为我们在实例化的时候,可以只给指定的部分字段赋值,没有赋值的字段则使用对应类型的零值。那么另一端在拿到字节流的时候,怎么知道哪些字段被赋了值,哪些字段没有被赋值呢?显然要通过序号来进行判断。

下面来编写.proto文件。

//文件名:girl.protosyntax="proto3";packagegirl;//枚举成员的值必须是整数enumGender{MALE=0;FEMALE=1;}messageUserInfo{stringname=1;int32age=2;Gendergender=3;}messageGirls{//列表里面的类型也可以是message定义的消息体repeatedUserInfogirls=1;}输入命令生成Python文件,然后导入测试:

importgirl_pb2user_info1=girl_pb2.UserInfo(name="古明地觉",age=17,gender=girl_pb2.Gender.Value("FEMALE"))user_info2=girl_pb2.UserInfo(name="芙兰朵露",age=400,#传入一个具体的值也是可以的gender=1)girls=girl_pb2.Girls(girls=[user_info1,user_info2])print(girls.girls[0].name,girls.girls[1].name)print(girls.girls[0].age,girls.girls[1].age)print(girls.girls[0].gender,girls.girls[1].gender)"""古明地觉芙兰朵露1740011"""枚举既可以定义在全局,也可以定义在某个message里面。

.proto文件的导入

.proto文件也可以互相导入,我们举个例子。下面定义两个文件,一个是people.proto,另一个是girl.proto,然后在girl.proto里面导入people.proto。

/*文件名:people.proto*/syntax="proto3";//此时的包名就很重要了,当该文件被其它文件导入时//需要通过这里的包名,来获取内部的消息体、枚举等数据packagepeople;messageBasicInfo{stringname=1;int32age=2;}/*文件名:girl.proto*/syntax="proto3";//导入people.proto,import"people.proto";messagePersonalInfo{stringphone=1;stringaddress=2;}messageGirl{//这里的BasicInfo是在people.proto里面定义的//people.proto里面的package指定的包名为people//所以这里需要通过people.的方式获取people.BasicInfobasic_info=1;PersonalInfopersonal_info=2;}然后执行命令,基于proto文件生成Python文件,显然此时会有两个Python文件。

python3-mgrpc_tools.protoc--python_out=.-I=.girl.proto

python3-mgrpc_tools.protoc--python_out=.-I=.people.proto

importgirl_pb2importpeople_pb2basic_info=people_pb2.BasicInfo(name="古明地觉",age=17)personal_info=girl_pb2.PersonalInfo(phone="18838888888",address="地灵殿")girl=girl_pb2.Girl(basic_info=basic_info,personal_info=personal_info)print(girl.basic_info.name)#古明地觉print(girl.basic_info.age)#17print(girl.personal_info.phone)#18838888888print(girl.personal_info.address)#地灵殿以上就是proto文件的导入,不复杂。

一些常用的方法

.proto文件在生成.py文件之后,里面的一个消息体对应一个类,我们可以对类进行实例化。而这些实例化的对象都有哪些方法呢?我们总结一下常用的。

首先重新编写girl.proto,然后生成Python文件。

syntax="proto3";messagePeople{stringname=1;int32age=2;}messageGirl{Peoplepeople=1;stringaddress=2;int32length=3;}内容很简单,我们测试一下。

小结

但是protobuf比json的性能要优秀很多,并且通过.proto文件定义好结构,约束性也要更强一些。

THE END
1.Python错题整理importmath result=math.isclose(2.1-2,0.1) 1 2 3 这样可以避免因浮点数精度问题导致的错误判断。 结论 由于浮点数精度问题,2.1 - 2不一定等于0.1,所以原命题是错误的。答案应为 F。 题目 Python中的成员运算符用于判断指定序列中是否包含某个值。 https://blog.csdn.net/xiaoyushashasha/article/details/144435477
2.python基础重点知识点笔记Python基础期末考试试题及答案(完整版)python基础重点梳理笔记.pdf Python2020期末考试试题及答案,pdf Python基础试题(含答案).pdf python基础试题(含答案)图文,pdf 1.Python应用基础 迅雷云盘?pan.xunlei.com/s/VODsrj__HDu4mqfWHp-oUwfSA1?pwd=fyba# Python变量和数据类型 https://zhuanlan.zhihu.com/p/12192479944
3.aiohttp.client—googleimport hdrs, http, payload from .abc import AbstractCookieJar from .client_exceptions import ClientConnectionError as ClientConnectionError from .client_exceptions import ( ClientConnectorCertificateError as ClientConnectorCertificateError, ) from .client_exceptions import ClientConnectorError as ClientConnectorhttps://googleapis.dev/python/google-auth/1.22.0/_modules/aiohttp/client.html
4.tornado.gen—Tornado5.1.1documentation[docs]defcoroutine(func):"""Decorator for asynchronous generators.Any generator that yields objects from this module must be wrappedin either this decorator or `engine`.Coroutines may "return" by raising the special exception`Return(value) <Return>`. In Python 3.3+, it is also possible forthehttps://www.tornadoweb.org/en/branch5.1/_modules/tornado/gen.html
5.python中级知识.md·空虚的心/blogfrom abc import ABCMeta, abstractmethod #抽象主题 class Oberserver(metaclass=ABCMeta): @abstractmethod def update(self): pass #具体主题 class Notice: def __init__(self): self.observers = [] def attach(self,obs): self.observers.append(obs) def detach(self,obs): self.observers.remove(obs)https://gitee.com/kongxudexin/blog/blob/master/python%E4%B8%AD%E7%BA%A7%E7%9F%A5%E8%AF%86.md
6.typing—bidict0.21.3documentationWrapper submodules for re and io related types."""fromabcimportabstractmethod,ABCMetaimportcollectionsimportcollections.abcimportcontextlibimportfunctoolsimportoperatorimportreasstdlib_re# Avoid confusion with the re we export.importsysimporttypesfromtypesimportWrapperDescriptorType,MethodWrapperType,Methodhttps://bidict.readthedocs.io/en/v0.21.3/_modules/typing.html
7.天津网站建设泰姆仕/宁波seo优化公司排名(3)abc里面所有的抽象基类 fromcollections.abcimport* 所有的抽象基类 #Copyright 2007 Google, Inc. All Rights Reserved.#Licensed to PSF under a Contributor Agreement."""Abstract Base Classes (ABCs) for collections, according to PEP 3119.Unit tests are in test_collections."""fromabcimportABCMeta,http://www.mhkc.cn/news/832195.html
8.Python中应用protobuf的示例详解pythonfrom abc import ABC from tornado import web, ioloop import girl_pb2 class GetInfoHandler(web.RequestHandler, ABC): async def post(self): # 拿到客户端传递的字节流 # 这个字节流应该是由 girl_pb2.Request() 序列化得到的 content = self.request.body # 下面进行反序列化 request = girl_pb2.Rehttps://www.jb51.net/article/275753.htm
9.Python进阶Python中的自定义装饰器:打造可重用的函数修饰from abc import ABC, abstractmethod # 抽象产品类 class Animal(ABC): @abstractmethod def make_sound(self): pass # 工厂类 class AnimalFactory: @staticmethod def create_animal(animal_type): if animal_type == "dog": return Dog() elif animal_type == "cat": https://cloud.tencent.com/developer/news/1353778
10.python2.6的新功能—Python3.10.0a4文档明智地检查abc,并且只在绝对必要的地方进行。 您可以使用 abc.ABCMeta 作为类定义中的元类: from abc import ABCMeta, abstractmethod class Drawable(): __metaclass__ = ABCMeta @abstractmethod def draw(self, x, y, scale=1.0): pass def draw_doubled(self, x, y): self.draw(x, y, scale=2.0https://www.osgeo.cn/cpython/whatsnew/2.6.html
11.Python基础(2)from abc import ABCMeta, abstractmethod class Super(metaclass = ABCMeta): @abstractmethod def action(self): pass x = Super() # 返回 TypeError: Can't instantiate abstract class Super with abstract methods action #-- # OOP和继承: "is-a"的关系 class Ahttps://www.jianshu.com/p/904b914cd3f6
12.FastApi(自用脚手架)+Snowy搭建后台管理系统(6)脚手架from abc import ABC, abstractmethod from typing import Union, Tuple from snowy_src.snowy_system.snowy_modules.auth.schemas import AuthAccountPasswordLoginParam, \ AuthPhoneValidCodeLoginParam from fastapi import Request class IAuthService(ABC): https://blog.itpub.net/70041327/viewspace-3037631/
13.elementplusimport所有语言mob64ca1400bfa8的技术博客elementplus import所有语言 第三章 栈、队列和数组 一、栈 栈是只能在一端进行插入和删除的线性表。 (别看只是个定义,非常重要,已经道出了运算方法:只能在一端插入和删除。) 栈的特征:后进先出,先进后出。 插入和删除元素的一端称为栈顶。(说明了我们在栈顶操作)https://blog.51cto.com/u_16213618/12794215
14.字符串の宝典name = 'abcdef' print(name[0]) print(name[1]) print(name[2]) # 索引是通过下标取某一个元素 # 切片是通过下标去某一段元素 s = 'Hello World!' print(s) print(s[4]) # o 字符串里的第4个元素 print(s[3:7]) # lo W 包含下标 3,不含下标 7 https://www.bilibili.com/read/cv39995475
15.What’sNewinPython2.6—Python3.13.1documentationYou can write your own ABCs by using abc.ABCMeta as the metaclass in a class definition: from abc import ABCMeta, abstractmethod class Drawable(): __metaclass__ = ABCMeta @abstractmethod def draw(self, x, y, scale=1.0): pass def draw_doubled(self, x, y): self.draw(x, y, scalehttps://docs.python.org/whatsnew/2.6.html
16.实例化类时abstractmethod()的作用和影响分析from abc import ABC, abstractmethod class Shape(ABC): @abstractmethod def area(self): pass class Rectangle(Shape): def __init__(self, width, height): self.width = width self.height = height def area(self): return self.width * self.height https://www.6qe.net/amg-article-131069-kp2lv4otuS.html
17.2.7.0详细内容见gitee·thomasjimmyfrom abc import abstractmethod from re import sub from typing import Union, Tuple, List from urllib.parse import quote from lxml.html import HtmlElement from selenium.webdriver.remote.webelement import WebElement @@ -330,6 +331,18 @@ def url_available(self) -> bool: """返回当前访问的url有https://github.com/thomas-jimmy-chen/DrissionPage/commit/2ca706c2f465a41479bcb8fd96ab431d68059883
18.python爬虫思维导图模板fromcollections.abcimportIterable print(isinstance([],Iterable)) >>True zip() zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表 字典 1.键值对,键不可以重复,值可以重复 2.元素是无序的 https://www.processon.com/view/6348e3687d9c080c42558dc6
19.ExtinguisherSizesofMarineSafetyandLifesavingfromQuality Marine Stainless Steel ABC Type Co2 Fire Extinguisher Sizes - find quality Marine Safety and Lifesaving, Fire Extinguisher & Marine Safety and Lifesaving from Chongqing B.M. Import & Export Trading Co., Ltd. of China Suppliers - 138511233.https://detail.en.china.cn/provide/p138511233.html
20.使用abc.def包中的所有类的方法可行的有()A. 组织单元为行,工作单元为列 B. 工作单元为行,绢织单元为列 C. 合同单元为行,活动单元为列 D. 活动单元为行,合同单元为列 查看完整题目与答案 参考解析: 设置CLASSPATH=/abc/def;源文件加入importabc.def.*;每个使用abc.def类的地方,在类名前加上abc.def AI解析 重新生成最新https://www.shuashuati.com/ti/37873e474a124645bd49900b68cec28e.html?fm=bdbds20956e29d77949e14ab490f3fd8c7ed0
21.国际贸易实务双语教程练习答案解析But as a form of international trade, it is still attractive in developing countries where foreign exchange is in short supply and inflow of foreign funds is far from sufficient to meet their obligations in external trade. Unit 2 General Procedures of Export and Import Transaction I. Answer thehttps://easylearn.baidu.com/edu-page/tiangong/exercisedetail?id=f541b7fea22d7375a417866fb84ae45c3a35c279&fr=search