Python使⽤Protobuf如何赋值如何正反序列化前⾔
使⽤protobuf主要是两个步骤,序列化和反序列化。
关于Proto有哪些数据类型,然后如何编写,此处就不赘述了,百度⼀下有很多。
此⽂主要是总结,python使⽤protobuf的过程,如何序列化和反序列化,对不同类型的字段如何进⾏赋值。
序列化
下⾯将⼀⼀列举各数据类型,在python中如何正确赋值。
⾸先,得把编译包给导⼊
import test_pb2 as pb
我分为两部分,分别为未被repeated修饰的字段 和 被repeated修饰后的字段
⽆修饰符
字符串
test.proto
message SearchService {
string type = 1;
}
创建message对象,然后赋值即可。与python中,通过类创建实例,实例.属性的⽅式进⾏赋值类似
search_service = pb.SearchService()enum怎么用
pe = "request"
数字型
test.proto
message SearchService {
int32 id = 2;
}
与字符串赋值⼀致
search_service = pb.SearchService()
search_service.id = 1
Message
test.proto
message SearchService {
// 定义⼀个message类型
message SearchRequest {
string content = 1;
string keyword = 2;
}
//  类型字段名序号
SearchRequest searchRequest = 3;
}
我们看到在SearchService⾥序号为3的字段的类型为SearchRequest,这是我们新定义的message
如果把message看作是⼀个类,那么我将其实例化,然后赋值给对应的字段,可以吗?
ok,这是不⾏的,错误⽰例:
search_service = pb.SearchService()
# 实例化SearchRequest
search_request = pb.SearchService.SearchRequest()
# 为search_request内部字段赋值
t = "hello protobuf"
search_request.keyword = "mk"
# 为search_service的searchRequest字段赋值
search_service.searchRequest = search_request
不允许在协议消息对象中分配复合字段“searchRequest”。
正确⽰例:
import test_pb2 as pb
search_t = "hello protobuf!"
search_service.searchRequest.keyword = "mk"
如果加上之前的那个字段,那么这样的:
import test_pb2 as pb
pe = "request"
search_service.id = 1
search_t = "hello protobuf!"
search_service.searchRequest.keyword = "mk"
Enum
枚举类型,注意⼀点:必须包含⼀个含0的字段
test.proto
syntax = "proto3";
message SearchService {
enum SearchType {
A = 0;
B = 1;
}
SearchType searchType = 4;
}
序号为4,类型为SearchType的枚举类,名为searchType的字段
此处的枚举类型,你可以看作是⽹页中的单选框,只能从给定的数据中选择⼀个,不选则默认为0 # ⼿动选择1
search_service.searchType = 1
# 或者是根据字段名进⾏选择
search_service.searchType = pb.SearchService.SearchType.A
被repeated修饰的字段
字符串或数字
test.proto
syntax = "proto3";
message SearchService {
# 修饰符类型字段名序号
repeated int32 uid = 5;
}
uid的类型是int32,然后被repeated修饰,即这个字段是可重复赋值的。
那么,在python中应该怎么赋值呢?
错误⽰例:
search_service.uid = 0
如果还是和之前⼀样的赋值,就会报错
AttributeError: Assignment not allowed to repeated field "uid" in protocol message object.正确⽰例:
search_service.uid.append(1)
search_service.uid.append(2)
所以,你可以将被repeated修饰的字段看作是⼀个空列表,往⾥添加值即可!
Message
test.proto
syntax = "proto3";
message SearchService {
message Second {
string type = 1;
string word = 2;
}
repeated Second seconds = 6;
}
seconds字段是可重复的message类型,在python中该如何赋值?
# 实例化⼀个second
second = search_service.Second()
# 为second对象赋值
second.word = 'world'
# 添加⾄seconds列表中
search_service.seconds.append(second)
或者,你也可以这样
search_service.seconds.append(
search_service.Second(type='efg', word="world")
)
或者这样:
seconds = [
search_service.Second(type='1', word="world"),
search_service.Second(type='2', word="world")
]
search_d(seconds)
所以,repeated修饰的字段,在python中,就是⼀个列表
Enum
test.ptoto
syntax = "proto3";
message SearchService {
enum SortOrder {
key1 = 0;
key2 = 1;
key3 = 2;
}
repeated SortOrder sortOrder = 7;
}
使⽤⽅法与之前的完全⼀致
sortFields = [
# 此处key1 根据关键词,获取枚举值
search_service.SortOrder.key1,
search_service.SortOrder.key2
]
search_d(sortFields)
现在我们已经全部赋值好了,接着就是序列化了
b = search_service.SerializeToString()
print(b)
# b'\n\x07request\x10\x01\x1a\x15\n\x0fhello protobuf!\x12\x02mk
# \x02*\x02\x01\x022\x0c\n\x03abc\x12\x05world2\x0c\n\x03efg
# \x12\x05world2\n\n\x011\x12\x05world2\n\n\x012\x12\x05world:\x02\x00\x01'
SerializeToString
Serializes the protocol message to a binary string.
序列化此协议消息为⼆进制串
反序列化
现在,我们是接收⽅,我们收到了⼀串⼆进制。
⾸先,我们需要使⽤将其反序列化,同样使⽤编译包。
search_service = pb.SearchService()
b = b'\n\x07request\x10\x01\x1a\x15\n\x0fhello protobuf!\x12\x02mk \x02*\x02\x01\x022\x0c\n\x03abc\x12\x05world2\x0c\n\x03efg\x12\x05world2\
n\n\x011\x12\x05world2\n\n\x012\x12\x05world:\x02\x00\x01'
search_service.ParseFromString(b)
# 访问属性值
print(pe) # 输出:request
ParseFromString解析函数
此时,search_service就已经含有传输过来的全部数据了。如果你不想使⽤对象.属性的⽅式调⽤,或者想使⽤类似json.loads直接转为python中的字典,那么你可以使
⽤protobuf_to_dict将其转为字典。
安装protobuf3_to_dict`
pip install protobuf3_to_dict
# 调⽤
from protobuf_to_dict import protobuf_to_dict
b = b'\n\x07request\x10\x01\x1a\x15\n\x0fhello protobuf!\x12\x02mk \x02*\x02\x01\x022\x0c\n\x03abc\x12\x05world2\x0c\n\x03efg\x12\x05world2\
n\n\x011\x12\x05world2\n\n\x012\x12\x05world:\x02\x00\x01'
search_service.ParseFromString(b)
# print(pe)
d = protobuf_to_dict(search_service)
print(d, type(d))
# {'type': 'request', 'id': 1, 'searchRequest': {'content': 'hello protobuf!', 'keyword': 'mk'}, 'searchType': 2, 'uid': [1, 2], 'seconds': [{'type': 'abc', 'word': 'world'}, {'type': 'efg', 'word': 'world'}, {'type': '1', 'word': 'world'}, {'type': '2', 'word': 'world'}], 'sortOrder': [0, 1]} <class 'dict'>
⼩⼩尝试
本⽂中例⼦,我做了⼀个接⼝。
接⼝地址: 47.101.154.110:8000/
你可以使⽤postman
提交数据,来查看结果也可以使⽤Python发送请求
import requests
headers = {
'Content-Type': 'application/grpc-web+proto'
}
b = b'\n\x07request\x10\x01\x1a\x15\n\x0fhello protobuf!\x12\x02mk \x02*\x02\x01\x022\x0c\n\x03abc\x12\x05world2\x0c\n\x03efg\x12\x05world2\n\n\x011\x12\x05world2\n\n\x012\x12\x05world:\x02\x00\x01'
resp = requests.post('47.101.154.110:8000/', data=b, headers=headers)
)
完整的test.proto
syntax = "proto3";
message SearchService {
string type = 1;
int32 id = 2;
// 定义⼀个message 类型
message SearchRequest {
string content = 1;
string keyword = 2;
}
//  类型        字段名      序号
SearchRequest searchRequest = 3;
enum SearchType {请求头
请求体请求⽅式必须指定Content-Type: application/grpc-web+proto
序列化后的⼆进制POST