python反序列化总结_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()

search_service.type = "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内部字段赋值

search_request.content = "hello protobuf"

search_request.keyword = "mk"

# 为search_service的searchRequest字段赋值

search_service.searchRequest = search_request

VRRzUv.png

不允许在协议消息对象中分配复合字段“searchRequest”。

正确示例:

import test_pb2 as pb

search_service.searchRequest.content = "hello protobuf!"

search_service.searchRequest.keyword = "mk"

如果加上之前的那个字段,那么这样的:

import test_pb2 as pb

search_service.type = "request"

search_service.id = 1

search_service.searchRequest.content = "hello protobuf!"

search_service.searchRequest.keyword = "mk"

Enum

枚举类型,注意一点:必须包含一个含0的字段

6vuMRr.png

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.type = 'abc'

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_service.seconds.extend(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_service.sortOrder.extend(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(search_service.type) # 输出: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(search_service.type)

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]}

小小尝试

本文中例子,我做了一个接口。

接口地址: http://47.101.154.110:8000/

请求头

请求体

请求方式

必须指定Content-Type: application/grpc-web+proto

序列化后的二进制

POST

你可以使用postman提交数据,来查看结果

vIbIja.gif

也可以使用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('http://47.101.154.110:8000/', data=b, headers=headers)

print(resp.text)

完整的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 {

A = 0;

B = 1;

}

SearchType searchType = 4;

repeated int32 uid = 5;

message Second {

string type = 1;

string word = 2;

}

repeated Second seconds = 6;

enum SortOrder {

key1 = 0;

key2 = 1;

key3 = 2;

}

repeated SortOrder sortOrder = 7;

}

完整的赋值示例

import test_pb2 as pb

from protobuf_to_dict import protobuf_to_dict

search_service = pb.SearchService()

search_service.type = "request"

search_service.id = 1

search_service.searchRequest.content = "hello protobuf!"

search_service.searchRequest.keyword = "mk"

# search_service.searchType = pb.SearchService.SearchType.A

search_service.searchType = 2

search_service.uid.append(1)

search_service.uid.append(2)

second = search_service.Second()

second.type = 'abc'

second.word = 'world'

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_service.seconds.extend(seconds)

sortFields = [

search_service.SortOrder.key1,

search_service.SortOrder.key2

]

search_service.sortOrder.extend(sortFields)

b = search_service.SerializeToString()

print(b)

推荐模块

在使用编译包时,没有代码提示,还有点不习惯。

这里,推荐安装mypy-protobuf

pip install mypy-protobuf

使用方法:

在你使用protoc命令编译proto文件时,新增一个参数mypy-out=,就像这样

protoc --python_out=. --mypy-out=. test.proto

此时会生成两个文件,并将他们拖入项目中的同一目录

test_pb2.py:我们需要导入使用的编译包

test_pb2.pyi:存根文件,在编辑器中会有代码提示(想了解存根文件,可以看最下面的参考文章)

效果演示:

zERFje.gif

参考文章

#感谢您访问本站#

#本文转载自互联网,若侵权,请联系删除,谢谢!657271#qq.com#

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值