Python中應(yīng)用protobuf的示例詳解
楔子
本次我們來(lái)聊一聊 protobuf,它是一個(gè)數(shù)據(jù)序列化和反序列化協(xié)議,因此它和 json 的定位是一樣的。當(dāng)客戶端需要傳遞數(shù)據(jù)給服務(wù)端時(shí),會(huì)將內(nèi)存中的對(duì)象序列化成一個(gè)可以在網(wǎng)絡(luò)中傳輸?shù)亩M(jìn)制流,服務(wù)端收到之后再反序列化,得到內(nèi)存中的對(duì)象。
不過(guò)既然都有 json 了,還會(huì)出現(xiàn) protobuf,那就說(shuō)明 protobuf 相較于 json 有著很大的優(yōu)勢(shì)。來(lái)看一下優(yōu)缺點(diǎn):

總結(jié)一下,protobuf 全稱為 Protocol Buffer,它是 Google 開(kāi)發(fā)的一種輕量并且高效的結(jié)構(gòu)化數(shù)據(jù)存儲(chǔ)格式,性能要遠(yuǎn)遠(yuǎn)優(yōu)于 json 和 xml。另外 protobuf 經(jīng)歷了兩個(gè)版本,分別是 protobuf2 和 protobuf3,目前主流的版本是 3,因?yàn)楦右子谩?/p>
下面就來(lái)開(kāi)始學(xué)習(xí) protobuf 吧。
但是別忘記安裝,直接 pip3 install grpcio grpcio-tools protobuf 即可
編寫(xiě)一個(gè)簡(jiǎn)單的 protobuf 文件
protobuf 文件有自己的語(yǔ)法格式,所以相比 json 它的門檻要高一些。我們創(chuàng)建一個(gè)文件,文件名為 girl.proto。
protobuf 文件的后綴是 .proto
//?syntax?負(fù)責(zé)指定使用哪一種?protobuf?服務(wù)
//?注意:syntax 必須寫(xiě)在非注釋的第一行
syntax?=?"proto3";
//?包名,?這個(gè)目前不是很重要,?你刪掉也是無(wú)所謂的
package?girl;
//?把 UserInfo 當(dāng)成 Python 中的類
//?name 和 age 當(dāng)成綁定在實(shí)例上的兩個(gè)屬性
message?UserInfo?{
??string?name?=?1;??//?=?1表示第1個(gè)參數(shù)
??int32?age?=?2;
}
protobuf 文件編寫(xiě)完成,然后我們要用它生成相應(yīng)的 Python 文件,命令如下:

我們要用 protobuf 文件生成 Python 文件,所以 --python_out 負(fù)責(zé)指定 Python 文件的輸出路徑,這里是當(dāng)前目錄;-I 表示從哪里尋找 protobuf 文件,這里也是當(dāng)前目錄;最后的 girl.proto 就是指定的 protobuf 文件了。
我們執(zhí)行該命令,會(huì)發(fā)現(xiàn)執(zhí)行完之后多了一個(gè) girl_pb2.py,我們直接用即可。注意:這是基于 protobuf 自動(dòng)生成的 Python 文件,我們不要修改它。如果參數(shù)或返回值需要改變,那么應(yīng)該修改 protobuf 文件,然后重新生成 Python 文件。
然后我們來(lái)看看采用 protobuf 協(xié)議序列化之后的結(jié)果是什么,不是說(shuō)它比較高效嗎?那么怎能不看看它序列化之后的結(jié)果呢,以及它和 json 又有什么不一樣呢?
import?orjson
import?girl_pb2
#?在?protobuf?文件中定義了?message?UserInfo
#?那么我們可以直接實(shí)例化它,而參數(shù)則是?name?和?age
#?因?yàn)樵?message?UserInfo?里面指定的字段是?name?和?age
user_info?=?girl_pb2.UserInfo(name="satori",?age=17)
#?如果不使用?protobuf,那么我們會(huì)選擇創(chuàng)建一個(gè)字典
user_info2?=?{"name":?"satori",?"age":?17}
#?然后來(lái)看看序列化之后的結(jié)果
#?調(diào)用?SerializeToString?方法會(huì)得到序列化之后的字節(jié)串
print(user_info.SerializeToString())
"""
b'\n\x06satori\x10\x11'
"""
#?如果是?json?的話
print(orjson.dumps(user_info2))
"""
b'{"name":"satori","age":17}'
"""可以看到使用 protobuf 協(xié)議序列化之后的結(jié)果要比 json 短,平均能得到一倍的壓縮。序列化我們知道了,那么如何反序列化呢?
import?orjson
import?girl_pb2
#?依舊是實(shí)例化一個(gè)對(duì)象,但是不需要傳參
user_info?=?girl_pb2.UserInfo()
#?傳入序列化之后的字節(jié)串,進(jìn)行解析(反序列化)
user_info.ParseFromString(b'\n\x06satori\x10\x11')
print(user_info.name)??#?satori
print(user_info.age)??#?17
#?json?也是同理,通過(guò) loads 方法反序列化
user_info2?=?orjson.loads(b'{"name":"satori","age":17}')
print(user_info2["name"])??#?satori
print(user_info2["age"])??#?17所以無(wú)論是 protobuf 還是 json,都是將一個(gè)對(duì)象序列化成二進(jìn)制字節(jié)串。然后根據(jù)序列化之后的字節(jié)串,再反序列出原來(lái)的對(duì)象。只不過(guò)采用 protobuf 協(xié)議進(jìn)行序列化和反序列化,速度會(huì)更快,并且序列化之后的數(shù)據(jù)壓縮比更高,在傳輸?shù)臅r(shí)候耗時(shí)也會(huì)更少。
然后還有一個(gè)關(guān)鍵地方的就是,json 這種數(shù)據(jù)結(jié)構(gòu)比較松散。你在返回 json 的時(shí)候,需要告訴調(diào)用你接口的人,返回的 json 里面都包含哪些字段,以及類型是什么。但 protobuf 則不需要,因?yàn)樽侄斡心男⒁约跋鄳?yīng)的類型,都必須在文件里面定義好。別人只要拿到 .proto 文件,就知道你要返回什么樣的數(shù)據(jù)了,一目了然。
在服務(wù)端之間傳輸 protobuf
如果兩個(gè)服務(wù)需要彼此訪問(wèn),那么最簡(jiǎn)單的方式就是暴露一個(gè) HTTP 接口,服務(wù)之間發(fā)送 HTTP 請(qǐng)求即可彼此訪問(wèn),至于請(qǐng)求數(shù)據(jù)和響應(yīng)數(shù)據(jù),則使用 JSON。
所以通過(guò) HTTP + JSON 是最簡(jiǎn)單的方式,也是業(yè)界使用最多的方式。但這種方式的性能不夠好,如果是同一個(gè)內(nèi)網(wǎng)的多個(gè)服務(wù),那么更推薦使用 gRPC + protobuf。關(guān)于 gRPC 以后再聊,我們來(lái)看看 protobuf 數(shù)據(jù)在 HTTP 請(qǐng)求中是如何傳遞的。
首先還是編寫(xiě) .proto 文件。
//?文件名:girl.proto
syntax?=?"proto3";
package?girl;
message?Request?{
????string?name?=?1;??
????int32?age?=?2;
}
message?Response?{
????string?info?=?1;
}一個(gè) protobuf 文件中可以定義任意個(gè) message,在生成 Python 文件之后每個(gè) message 會(huì)對(duì)應(yīng)一個(gè)同名的類。然后我們執(zhí)行之前的命令,生成 Python 文件。
接下來(lái)使用 Tornado 編寫(xiě)一個(gè)服務(wù):
from?abc?import?ABC
from?tornado?import?web,?ioloop
import?girl_pb2
class?GetInfoHandler(web.RequestHandler,?ABC):
????async?def?post(self):
????????#?拿到客戶端傳遞的字節(jié)流
????????#?這個(gè)字節(jié)流應(yīng)該是由?girl_pb2.Request()?序列化得到的
????????content?=?self.request.body
????????#?下面進(jìn)行反序列化
????????request?=?girl_pb2.Request()
????????request.ParseFromString(content)
????????#?獲取里面的?name?和?age?字段的值
????????name?=?request.name
????????age?=?request.age
????????#?生成?Response?對(duì)象
????????response?=?girl_pb2.Response(
????????????info=f"name:?{name},?age:?{age}"
????????)
????????#?但?Response?對(duì)象不能直接返回,需要序列化
????????return?await?self.finish(response.SerializeToString())
app?=?web.Application(
????[("/get_info",?GetInfoHandler)]
)
app.listen(9000)
ioloop.IOLoop.current().start()整個(gè)過(guò)程很簡(jiǎn)單,和 JSON 是一樣的。然后我們來(lái)訪問(wèn)一下:
import?requests
import?girl_pb2
#?往?localhost:9000?發(fā)請(qǐng)求
#?參數(shù)是?girl_pb2.Request()?序列化后的字節(jié)流
payload?=?girl_pb2.Request(
????name="古明地覺(jué)",?age=17
).SerializeToString()
#?發(fā)送?HTTP?請(qǐng)求,返回?girl_pb2.Response()?序列化后的字節(jié)流
content?=?requests.post("http://localhost:9000/get_info",
????????????????????????data=payload).content
#?然后我們反序列化
response?=?girl_pb2.Response()
response.ParseFromString(content)
print(response.info)
"""
name:?古明地覺(jué),?age:?17
"""所以 protobuf 本質(zhì)上也是一個(gè)序列化和反序列化協(xié)議,在使用上和 JSON 沒(méi)有太大區(qū)別。只不過(guò) JSON 對(duì)應(yīng)的 Python 對(duì)象是字典,而 protobuf 則是單獨(dú)生成的對(duì)象。
protobuf 的基礎(chǔ)數(shù)據(jù)類型
在不涉及 gRPC 的時(shí)候,protobuf 文件是非常簡(jiǎn)單的,你需要返回啥結(jié)構(gòu),那么直接在 .proto 文件里面使用標(biāo)識(shí)符 message 定義即可。
message 消息名稱 {
類型 字段名 = 1;
類型 字段名 = 2;
類型 字段名 = 3;
}
但是類型我們需要說(shuō)一下,之前用到了兩個(gè)基礎(chǔ)類型,分別是 string 和 int32,那么除了這兩個(gè)還有哪些類型呢?

以上是基礎(chǔ)類型,當(dāng)然還有復(fù)合類型,我們一會(huì)單獨(dú)說(shuō),先來(lái)演示一下基礎(chǔ)類型。編寫(xiě) .proto 文件:
//?文件名:basic_type.proto
syntax?=?"proto3";
package?basic_type;
message?BasicType?{
????//?字段的名稱可以和類型名稱一致,這里為了清晰
????//?我們就直接將類型的名稱用作字段名
????int32?int32?=?1;
????sint32?sint32?=?2;
????uint32?uint32?=?3;
????fixed32?fixed32?=?4;
????sfixed32?sfixed32?=?5;
????int64?int64?=?6;
????sint64?sint64?=?7;
????uint64?uint64?=?8;
????fixed64?fixed64?=?9;
????sfixed64?sfixed64?=?10;
????double?double?=?11;
????float?float?=?12;
????bool?bool?=?13;
????string?string?=?14;
????bytes?bytes?=?15;
}然后我們來(lái)生成 Python 文件,命令如下:
python3 -m grpc_tools.protoc --python_out=. -I=. basic_type.proto
執(zhí)行之后,會(huì)生成 basic_type_pb2.py 文件,我們測(cè)試一下:
import?basic_type_pb2 basic_type?=?basic_type_pb2.BasicType( ????int32=123, ????sint32=234, ????uint32=345, ????fixed32=456, ????sfixed32=789, ????int64=1230, ????sint64=2340, ????uint64=3450, ????fixed64=4560, ????sfixed64=7890, ????double=3.1415926, ????float=2.71, ????bool=True, ????string="古明地覺(jué)", ????bytes=b"satori", ) #?定義一個(gè)函數(shù),接收序列化之后的字節(jié)流 def?parse(content:?bytes): ????obj?=?basic_type_pb2.BasicType() ????#?反序列化 ????obj.ParseFromString(content) ????print(obj.int32) ????print(obj.sfixed64) ????print(obj.string) ????print(obj.bytes) ????print(obj.bool) parse(basic_type.SerializeToString()) """ 123 7890 古明地覺(jué) b'satori' True """
很簡(jiǎn)單,沒(méi)有任何問(wèn)題,以上就是 protobuf 的基礎(chǔ)類型。然后再來(lái)看看符合類型,以及一些特殊類型。
repeat 和 map
repeat 和 map 是一種復(fù)合類型,可以把它們當(dāng)成 Python 的列表和字典。
//?文件名:girl.proto
syntax?=?"proto3";
package?girl;
message?UserInfo?{
????//?對(duì)于?Python?而言
????//?repeated?表示?hobby?字段的類型是列表
????//?string?則表示列表里面的元素必須都是字符串
????repeated?string?hobby?=?1;???
????//?map<string,?string>?表示?info?字段的類型是字典
????//?字典的鍵值對(duì)必須都是字符串
????map<string,?string>?info?=?2;
}我們執(zhí)行命令,生成 Python 文件,然后導(dǎo)入測(cè)試一下。
import?girl_pb2
user_info?=?girl_pb2.UserInfo(
????hobby=["唱",?"跳",?"rap",?"??"],
????info={"name":?"古明地覺(jué)",?"age":?"17"}
)
print(user_info.hobby)
print(user_info.info)
"""
['唱',?'跳',?'rap',?'??']
{'name':?'古明地覺(jué)',?'age':?'17'}
"""結(jié)果正常,沒(méi)有問(wèn)題。但需要注意:對(duì)于復(fù)合類型而言,在使用的時(shí)候有一個(gè)坑。
import?girl_pb2
#?如果我們沒(méi)有給字段傳值,那么會(huì)有一個(gè)默認(rèn)的零值
user_info?=?girl_pb2.UserInfo()
print(user_info.hobby)??#?[]
print(user_info.info)??#?{}
#?對(duì)于復(fù)合類型的字段來(lái)說(shuō),我們不能單獨(dú)賦值
try:
????user_info.hobby?=?["唱",?"跳",?"rap",?"??"]
except?AttributeError?as?e:
????print(e)
"""
Assignment?not?allowed?to?repeated?field?"hobby"?in?protocol?message?object.
"""
#?先實(shí)例化,然后單獨(dú)給字段賦值,只適用于基礎(chǔ)類型
#?因此我們需要這么做
user_info.hobby.extend(["唱",?"跳",?"rap",?"??"])
user_info.info.update({"name":?"古明地覺(jué)",?"age":?"17"})
print(user_info.hobby)
print(user_info.info)
"""
['唱',?'跳',?'rap',?'??']
{'name':?'古明地覺(jué)',?'age':?'17'}
"""所以這算是一個(gè)需要注意的點(diǎn),也不能叫坑吧,總之注意一下即可。
message 的嵌套
通過(guò)標(biāo)識(shí)符 message 即可定義一個(gè)消息體,大括號(hào)里面的則是參數(shù),但參數(shù)的類型也可以是另一個(gè) message。換句話說(shuō),message 是可以嵌套的。
//?文件名:girl.proto
syntax?=?"proto3";
package?girl;
message?UserInfo?{
????repeated?string?hobby?=?1;
????//?BasicInfo?定義在外面也是可以的
????message?BasicInfo?{
????????string?name?=?1;
????????int32?age?=?2;
????????string?address?=?3;
????}
????BasicInfo?basic_info?=?2;
}生成 Python 文件,導(dǎo)入測(cè)試一下。
import?girl_pb2 #?在?protobuf?文件中,BasicInfo?定義在?UserInfo?里面 #?所以?BasicInfo?在這里對(duì)應(yīng)?UserInfo?的一個(gè)類屬性 #?如果定義在全局,那么直接通過(guò)?girl_pb2?獲取即可 basic_info?=?girl_pb2.UserInfo.BasicInfo( ????name="古明地覺(jué)",?age=17,?address="地靈殿") user_info?=?girl_pb2.UserInfo( ????hobby=['唱',?'跳',?'rap',?'??'], ????basic_info=basic_info ) print(user_info.hobby) """ ['唱',?'跳',?'rap',?'??'] """ print(user_info.basic_info.name) print(user_info.basic_info.age) print(user_info.basic_info.address) """ 古明地覺(jué) 17 地靈殿 """
以上是 message 的嵌套,或者說(shuō)通過(guò) message 定義的消息體,也可以作為字段的類型。
枚舉類型
再來(lái)聊一聊枚舉類型,它通過(guò) enum 標(biāo)識(shí)符定義。
//?里面定義了兩個(gè)成員,分別是?MALE?和?FEMALE
enum?Gender?{
????MALE?=?0;
????FEMALE?=?1;
}這里需要說(shuō)明的是,對(duì)于枚舉來(lái)說(shuō),等號(hào)后面的值表示成員的值。比如一個(gè)字段的類型是 Gender,那么在給該字段賦值的時(shí)候,要么傳 0 要么傳 1。因?yàn)槊杜e Gender 里面只有兩個(gè)成員,分別代表 0 和 1。
而我們前面使用 message 定義消息體的時(shí)候,每個(gè)字段后面跟著的值則代表序號(hào),從 1 開(kāi)始,依次遞增。至于為什么要有這個(gè)序號(hào),是因?yàn)槲覀冊(cè)趯?shí)例化的時(shí)候,可以只給指定的部分字段賦值,沒(méi)有賦值的字段則使用對(duì)應(yīng)類型的零值。那么另一端在拿到字節(jié)流的時(shí)候,怎么知道哪些字段被賦了值,哪些字段沒(méi)有被賦值呢?顯然要通過(guò)序號(hào)來(lái)進(jìn)行判斷。
下面來(lái)編寫(xiě) .proto 文件。
//?文件名:girl.proto
syntax?=?"proto3";
package?girl;
//?枚舉成員的值必須是整數(shù)
enum?Gender?{
????MALE?=?0;
????FEMALE?=?1;
}
message?UserInfo?{
????string?name?=?1;
????int32?age?=?2;
????Gender?gender?=?3;
}
message?Girls?{
????//?列表里面的類型也可以是?message?定義的消息體
????repeated?UserInfo?girls?=?1;
}輸入命令生成 Python 文件,然后導(dǎo)入測(cè)試:
import?girl_pb2
user_info1?=?girl_pb2.UserInfo(
????name="古明地覺(jué)",?age=17,
????gender=girl_pb2.Gender.Value("FEMALE"))
user_info2?=?girl_pb2.UserInfo(
????name="芙蘭朵露",?age=400,
????#?傳入一個(gè)具體的值也是可以的
????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)
"""
古明地覺(jué)?芙蘭朵露
17?400
1?1
"""枚舉既可以定義在全局,也可以定義在某個(gè) message 里面。
.proto 文件的導(dǎo)入
.proto 文件也可以互相導(dǎo)入,我們舉個(gè)例子。下面定義兩個(gè)文件,一個(gè)是 people.proto,另一個(gè)是 girl.proto,然后在 girl.proto 里面導(dǎo)入 people.proto。
/*?文件名:people.proto */
syntax?=?"proto3";
//?此時(shí)的包名就很重要了,當(dāng)該文件被其它文件導(dǎo)入時(shí)
//?需要通過(guò)這里的包名,來(lái)獲取內(nèi)部的消息體、枚舉等數(shù)據(jù)
package?people;
message?BasicInfo?{
????string?name?=?1;
????int32?age?=?2;
}
/*?文件名:girl.proto */
syntax?=?"proto3";
//?導(dǎo)入?people.proto,
import?"people.proto";
message?PersonalInfo?{
????string?phone?=?1;
????string?address?=?2;
}
message?Girl?{
????//?這里的?BasicInfo?是在?people.proto?里面定義的
????//?people.proto?里面的?package?指定的包名為?people
????//?所以這里需要通過(guò)?people.?的方式獲取
????people.BasicInfo?basic_info?=?1;
????PersonalInfo?personal_info?=?2;
}然后執(zhí)行命令,基于 proto 文件生成 Python 文件,顯然此時(shí)會(huì)有兩個(gè) Python 文件。
python3 -m grpc_tools.protoc --python_out=. -I=. girl.proto
python3 -m grpc_tools.protoc --python_out=. -I=. people.proto
import?girl_pb2 import?people_pb2 basic_info?=?people_pb2.BasicInfo(name="古明地覺(jué)",?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)??#?古明地覺(jué) print(girl.basic_info.age)??#?17 print(girl.personal_info.phone)??#?18838888888 print(girl.personal_info.address)??#?地靈殿
以上就是 proto 文件的導(dǎo)入,不復(fù)雜。
一些常用的方法
.proto 文件在生成 .py 文件之后,里面的一個(gè)消息體對(duì)應(yīng)一個(gè)類,我們可以對(duì)類進(jìn)行實(shí)例化。而這些實(shí)例化的對(duì)象都有哪些方法呢?我們總結(jié)一下常用的。
首先重新編寫(xiě) girl.proto,然后生成 Python 文件。
syntax?=?"proto3";
message?People?{
????string?name?=?1;
????int32?age?=?2;
}
message?Girl?{
????People?people?=?1;
????string?address?=?2;
????int32?length?=?3;
}內(nèi)容很簡(jiǎn)單,我們測(cè)試一下。
import?girl_pb2 girl?=?girl_pb2.Girl( ????people=girl_pb2.People(name="古明地覺(jué)",?age=17), ????address="地靈殿", ????length=152 ) # SerializeToString:將對(duì)象序列化成二進(jìn)制字節(jié)串 content?=?girl.SerializeToString() # ParseFromString:將二進(jìn)制字節(jié)串反序列化成對(duì)象 girl2?=?girl_pb2.Girl() girl2.ParseFromString(content) print( ????girl2.people.name, ????girl2.people.age, ????girl2.address, ????girl2.length )??#?古明地覺(jué)?17?地靈殿?152 #?以上兩個(gè)是最常用的方法 # MergeFrom:將一個(gè)對(duì)象合并到另一個(gè)對(duì)象上面 girl?=?girl_pb2.Girl(address="紅魔館",?length=145) #?我們先實(shí)例化了?Girl,后實(shí)例化?People #?接下來(lái)要將它綁定到?girl?的?people?字段上 people?=?girl_pb2.People(name="芙蘭朵露",?age=400) #?但?girl.people?=?people?是會(huì)報(bào)錯(cuò)的,因?yàn)橹挥袠?biāo)量才能這么做 #?所以我們可以通過(guò)?girl.people.xxx?=?people.xxx?進(jìn)行綁定 #?但如果?people?的字段非常多,那么會(huì)很麻煩 #?因此這個(gè)時(shí)候可以使用?MergeFrom girl.people.MergeFrom(people) print( ????girl.people.name,?girl.people.age )??#?芙蘭朵露?400 #?同理還有?MergeFromString,接收的是序列化之后的字節(jié)串 people.name,?people.age?=?"魔理沙",?15 girl.people.MergeFromString(people.SerializeToString()) print( ????girl.people.name,?girl.people.age )??#?魔理沙?15
非常簡(jiǎn)單,但我們發(fā)現(xiàn)還少了點(diǎn)什么,就是它和 Python 的字典能不能互相轉(zhuǎn)化呢?答案是可以的,但需要導(dǎo)入專門的函數(shù)。
from?google.protobuf.json_format?import?(
????MessageToJson,
????MessageToDict
)
import?girl_pb2
girl?=?girl_pb2.Girl(
????people=girl_pb2.People(name="古明地覺(jué)",?age=17),
????address="地靈殿",
????length=152
)
#?轉(zhuǎn)成?JSON
print(MessageToJson(girl))
"""
{
??"people":?{
????"name":?"\u53e4\u660e\u5730\u89c9",
????"age":?17
??},
??"address":?"\u5730\u7075\u6bbf",
??"length":?152
}
"""
#?轉(zhuǎn)成字典
print(MessageToDict(girl))
"""
{'people':?{'name':?'古明地覺(jué)',?'age':?17},?
?'address':?'地靈殿',?'length':?152}
"""同理,如果我們有一個(gè)字典,也可以轉(zhuǎn)成相應(yīng)的對(duì)象。
from?google.protobuf.json_format?import?(
????ParseDict
)
import?girl_pb2
data?=?{'people':?{'name':?'魔理沙',?'age':?16},
????????'address':?'魔法森林',?'length':?156}
girl?=?girl_pb2.Girl()
#?基于字典進(jìn)行解析
ParseDict(data,?girl)
print(girl.people.name)??#?魔理沙
print(girl.people.age)??#?16
print(girl.address)??#?魔法森林
print(girl.length)??#?156以上就是工作中的一些常用的方法。
小結(jié)
以上就是 protobuf 相關(guān)的內(nèi)容,核心就是編寫(xiě) .proto 文件,然后生成 Python 文件。它在業(yè)務(wù)中發(fā)揮的作用,和 json 是類似的,都是將對(duì)象轉(zhuǎn)成二進(jìn)制之后再通過(guò)網(wǎng)絡(luò)進(jìn)行傳輸。接收方在收到字節(jié)流之后,將其反序列化成內(nèi)存中的對(duì)象,然后獲取內(nèi)部的字段。
但是 protobuf 比 json 的性能要優(yōu)秀很多,并且通過(guò) .proto 文件定義好結(jié)構(gòu),約束性也要更強(qiáng)一些。
最后補(bǔ)充一點(diǎn),.proto 文件里面還可以定義很多和 gRPC 相關(guān)的內(nèi)容,關(guān)于 gRPC 我們以后再聊。
到此這篇關(guān)于Python中應(yīng)用protobuf的示例詳解的文章就介紹到這了,更多相關(guān)Python protobuf內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python+Pika+RabbitMQ環(huán)境部署及實(shí)現(xiàn)工作隊(duì)列的實(shí)例教程
RabbitMQ是一個(gè)消息隊(duì)列服務(wù)器,在本文中我們將學(xué)習(xí)到Python+Pika+RabbitMQ環(huán)境部署及實(shí)現(xiàn)工作隊(duì)列的實(shí)例教程,需要的朋友可以參考下2016-06-06
python GUI庫(kù)圖形界面開(kāi)發(fā)之PyQt5圖片顯示控件QPixmap詳細(xì)使用方法與實(shí)例
這篇文章主要介紹了python GUI庫(kù)圖形界面開(kāi)發(fā)之PyQt5圖片顯示控件QPixmap詳細(xì)使用方法與實(shí)例,需要的朋友可以參考下2020-02-02
Python 數(shù)據(jù)分析之Beautiful Soup 提取頁(yè)面信息
Beautiful Soup 提供一些簡(jiǎn)單的、python 式的函數(shù)用來(lái)處理導(dǎo)航、搜索、修改分析樹(shù)等功能。它是一個(gè)工具箱,通過(guò)解析文檔為用戶提供需要抓取的數(shù)據(jù),因?yàn)楹?jiǎn)單,所以不需要多少代碼就可以寫(xiě)出一個(gè)完整的應(yīng)用程序2021-10-10
用python 批量更改圖像尺寸到統(tǒng)一大小的方法
下面小編就為大家分享一篇用python 批量更改圖像尺寸到統(tǒng)一大小的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-03-03
Python?Collections庫(kù)的高級(jí)功能使用示例詳解
Python的collections庫(kù)提供了一系列有用的數(shù)據(jù)類型,擴(kuò)展了內(nèi)建的數(shù)據(jù)類型,為開(kāi)發(fā)者提供了更多高級(jí)功能,本文將深入探討collections庫(kù)的一些高級(jí)功能,通過(guò)詳細(xì)的示例代碼演示,幫助大家更好地理解和應(yīng)用這些功能2023-12-12
Python sqrt()函數(shù)用法說(shuō)明
這篇文章主要介紹了Python sqrt()函數(shù)用法說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-03-03
Python中使用正則表達(dá)式精準(zhǔn)匹配IP地址的案例
Python的正則表達(dá)式(re模塊)是完成這個(gè)任務(wù)的利器,但你知道怎么寫(xiě)才能準(zhǔn)確匹配各種合法的IP地址嗎,今天我們就來(lái)詳細(xì)探討這個(gè)問(wèn)題,感興趣的朋友一起看看吧2025-04-04

