首页 > Python资料 博客日记
protobuf学习和使用(Python)
2025-01-16 12:30:10Python资料围观12次
Protobuf学习
文章目录
简介
Protocol Buffers (Protobuf) 是一种由 Google 开发的高效、跨平台的序列化协议。它用于将结构化数据转换为紧凑的二进制格式,适用于网络通信、存储和跨语言的数据交换。Protobuf 支持多种编程语言,并允许在不破坏兼容性的情况下扩展数据结构。
主要特点:
- 高效:比 JSON 和 XML 更加节省空间和传输速度快。
- 跨平台和跨语言:支持多种编程语言(如 C++, Java, Python 等)。
- 可扩展:可以在不破坏旧版本兼容性的情况下添加新字段。
通过定义 .proto
文件来描述数据结构,然后使用 Protobuf 编译器生成代码进行序列化和反序列化。
安装
windows安装编译器
下载地址:https://github.com/protocolbuffers/protobuf/releases/download/v28.3/protobuf-28.3.zip
- 下载需要的protobuf编译器
- 下载完成后,解压,将\protoc-28.3-win64\bin目录添加到环境变量
- 应用后,在命令行输入
protoc --version
验证是否安装成功
Python安装Protobuf库
- pip install protobuf
example
先来一个简单的示例,了解怎么去使用它。
首先需要编写一个proto文件,如Person.proto
// 声明使用的版本信息
syntax = "proto3";
// 添加包名
package user;
// 这个定义描述了一个 Person 消息,其中包含了三种字段:name(字符串类型)、id(整数类型)、email(字符串类型)。
message Person {
string name = 1;
int32 age = 2;
string email = 3;
}
编译生成python文件
# python_out用于指定生成python文件
# =.表示生成的python文件在当前目录下
# Person.proto用于编译的proto源文件
protoc --python_out=. Person.proto
创建一个Python文件,用于创建和读取Person消息
import Person_pb2
# 创建一个对象
person = Person_pb2.Person()
person.name = "study_proto"
person.age = 3
person.email = "study_proto@163.com"
# 序列化消息 SerializeToString固定用法
data = person.SerializeToString()
print(f"序列化后的消息为{data}")
# 创建一个新的对象
new_person = Person_pb2.Person()
# 反序列化消息 ParseFromString固定用法
new_person.ParseFromString(data)
print(f"Name:{new_person.name}\r\nAge:{new_person.age}\r\nEmail:{new_person.email}")
运行此python文件,输出的消息如下
序列化后的消息为b'\n\x0bstudy_proto\x10\x03\x1a\x13study_proto@163.com'
Name:study_proto
Age:3
Email:study_proto@163.com
来一个说明讲一下上边做了什么事
- 首先我们编写了一个protobuf文件
- 然后通过proto编译器生成出来pb2文件
- 然后创建了一个python文件用于创建和读取Person消息
- 在这个python文件中 ,代码第10行,是进行序列化消息,可以理解为将你能看明白的消息给压缩了,这个时候只有proto能看明白这个消息是什么;这个时候的data我们没有实际的业务,如果有实际的业务他可以通过http请求发送给服务器,或者通过文件发送给用户,保存在本地,代码16行是把这个我们看不懂的消息进行反序列化,让这个我们看不懂的消息通过反序列化能够让我们看懂。
- protobuf是一个支持跨平台的语言,我们可以生成python的代码,也可以生成c或者c++的代码,我们在自己的代码进行序列化或者反序列化,把这个消息交给其他语言,他们也是能够直接进行处理的。
数据类型映射关系
基本数据类型映射
Python 数据类型 | Protobuf 数据类型 | 说明 |
---|---|---|
int | int32 / int64 | int32 和 int64 用于表示整数,int32 默认范围更小(-2^31 到 2^31-1),int64 范围更大(-2^63 到 2^63-1)。 |
float | float | 用于表示单精度浮点数。 |
double | double | 用于表示双精度浮点数。 |
str | string | 用于表示字符串。 |
bool | bool | 用于表示布尔值。 |
bytes | bytes | 用于表示字节序列。 |
复杂数据类型映射
Python 数据类型 | Protobuf 数据类型 | 说明 |
---|---|---|
list (Python的列表) | repeated <type> | 对应 Protobuf 中的 repeated 字段,用于表示可重复的元素。 type 是数据元素的类型。 |
dict (Python字典) | map<key_type, value_type> | 用于表示键值对集合。key_type 和 value_type 分别为键和值的类型。 |
tuple (Python元组) | 无法直接映射 | Python 中的元组没有直接的 Protobuf 对应类型,一般可以用 message 或 repeated 来代替 |
Protobuf 的 message
类型映射
Protobuf 中的 message
类型通常用于表示一个复杂的数据结构,Python 中可以使用类来表示。
Python 数据类型 | Protobuf 数据类型 | 说明 |
---|---|---|
class (Python类) | message | Protobuf 的 message 类型。每个字段都有自己的类型,可以是基本类型,也可以是嵌套的 message 类型。 |
proto2和proto3的区别
特性 | proto2 | proto3 |
---|---|---|
语法版本 | syntax = "proto2"; | syntax = "proto3"; |
字段默认值 | 支持字段的默认值,可以为字段设置默认值。 | 不支持字段的默认值,所有字段有默认值(如 0 、"" 、false )。 |
必选字段(required) | 支持 required 字段,表示该字段是必须的。 | 不支持 required 字段,所有字段都是可选的(optional)。 |
可选字段(optional) | 支持 optional 字段,字段可以选择不提供。 | optional 字段在 proto3 中默认为隐式的,无需显式声明。 |
枚举值的默认值 | 枚举类型支持一个默认值。 | 枚举的第一个值默认为默认值。 |
扩展(extensions) | 支持扩展字段,可以在未修改原始 .proto 文件的情况下,扩展消息结构。 | 不支持扩展(extensions)。 |
Any 类型 | 不支持 Any 类型。 | 支持 Any 类型,可以包含任何类型的数据。 |
未知字段 | 不会忽略未识别的字段,通常会导致解析错误。 | 自动忽略未知字段,允许向现有的消息中添加字段而不影响兼容性。 |
字段类型命名 | 支持 required 、optional 、repeated 类型字段。 | 只有 repeated 和 optional (隐式)类型字段。 |
重复字段(repeated) | 支持 repeated 字段,用于存储多个相同类型的值。 | 支持 repeated 字段,行为与 proto2 相同。 |
oneof | 支持 oneof ,用于表示多种选择中的一个字段。 | 支持 oneof ,行为与 proto2 相同。 |
默认值和初始化 | 每个字段都有显式的默认值,用户可以在 .proto 文件中指定。 | 默认值隐式地由类型决定(例如,数值类型默认为 0 ,字符串默认为 "" )。 |
注释 | 支持注释,用于代码生成时的文档。 | 支持注释,用于代码生成时的文档。 |
代码生成 | 生成的代码中会包含 required 、optional 和 default 。 | 生成的代码省略 required 和 default ,并且所有字段为隐式 optional 。 |
字段命名 | 字段名称可按要求自由命名。 | 字段名称也按要求自由命名,但更注重简洁性和兼容性。 |
案例
example1_定义基本的消息类型
- 定义一个
Person
消息,包含以下字段:- 姓名(
name
,字符串类型) - 年龄(
age
,整数类型) - 是否订阅了新闻邮件(
subscribed
,布尔类型)
- 姓名(
- 定义一个
Address
消息,包含:- 街道(
street
,字符串类型) - 城市(
city
,字符串类型) - 邮政编码(
zip_code
,字符串类型)
- 街道(
- 在
Person
消息中嵌套一个Address
字段。
Person.proto
syntax = "proto3";
package person;
message Address {
string street = 1;
string city = 2;
string zip_code = 3;
}
message Person {
string name = 1;
int32 age = 2;
bool subscribed = 3;
Address address = 4;
}
根据proto文件生成编译后的python文件
protoc --python_out=. Person.proto
main.py
import Person_pb2
# 创建一个Person对象
person = Person_pb2.Person()
person.name = "jason"
person.age = 18
person.subscribed = True
person.address.street = "人民街道"
person.address.city = "上海"
person.address.zip_code = "410000"
# 序列化消息
encode_data = person.SerializeToString()
print(encode_data)
# 创建一个新对象
new_person = Person_pb2.Person()
# 反序列化消息
new_person.ParseFromString(encode_data)
print(new_person)
运行输出
b'\n\x05jason\x10\x12\x18\x01"\x1e\n\x0c\xe4\xba\xba\xe6\xb0\x91\xe8\xa1\x97\xe9\x81\x93\x12\x06\xe4\xb8\x8a\xe6\xb5\xb7\x1a\x06410000'
name: "jason"
age: 18
subscribed: true
address {
street: "人民街道"
city: "上海"
zip_code: "410000"
}
example2_使用 repeated 字段
-
使用
repeated
字段-
定义一个
Library
消息,包含:- 图书名称列表(
book_titles
,repeated string
类型)
- 图书名称列表(
-
创建一个包含多个图书名称的
Library
实例,并将其序列化和反序列化。
-
- 使用
repeated
字段可以方便地存储多个相同类型的数据。 SerializeToString()
和ParseFromString()
分别用于序列化和反序列化。- Protobuf 在处理大规模数据交换时非常高效,特别适合跨语言的应用场景。
Library.proto
syntax = "proto3";
package library;
message Library{
repeated string book_titles = 1;
}
通过proto文件编译生成python文件
protoc --python_out=. Library.proto
main.py
import Library_pb2
library = Library_pb2.Library()
for i in range(1, 20):
library.book_titles.append(f"人民的名义{i}")
encode_data = library.SerializeToString()
library.ParseFromString(encode_data)
for i in library.book_titles:
print(i)
输出
b'\n\x10\xe4\xba\xba\xe6\xb0\x91\xe7\x9a\x84\xe5\x90\x8d\xe4\xb9\x891\n\x10\xe4\xba\xba\xe6\xb0\x91\xe7\x9a\x84\xe5\x90\x8d\xe4\xb9\x892\n\x10\xe4\xba\xba\xe6\xb0\x91\xe7\x9a\x84\xe5\x90\x8d\xe4\xb9\x893\n\x10\xe4\xba\xba\xe6\xb0\x91\xe7\x9a\x84\xe5\x90\x8d\xe4\xb9\x894\n\x10\xe4\xba\xba\xe6\xb0\x91\xe7\x9a\x84\xe5\x90\x8d\xe4\xb9\x895\n\x10\xe4\xba\xba\xe6\xb0\x91\xe7\x9a\x84\xe5\x90\x8d\xe4\xb9\x896\n\x10\xe4\xba\xba\xe6\xb0\x91\xe7\x9a\x84\xe5\x90\x8d\xe4\xb9\x897\n\x10\xe4\xba\xba\xe6\xb0\x91\xe7\x9a\x84\xe5\x90\x8d\xe4\xb9\x898\n\x10\xe4\xba\xba\xe6\xb0\x91\xe7\x9a\x84\xe5\x90\x8d\xe4\xb9\x899\n\x11\xe4\xba\xba\xe6\xb0\x91\xe7\x9a\x84\xe5\x90\x8d\xe4\xb9\x8910\n\x11\xe4\xba\xba\xe6\xb0\x91\xe7\x9a\x84\xe5\x90\x8d\xe4\xb9\x8911\n\x11\xe4\xba\xba\xe6\xb0\x91\xe7\x9a\x84\xe5\x90\x8d\xe4\xb9\x8912\n\x11\xe4\xba\xba\xe6\xb0\x91\xe7\x9a\x84\xe5\x90\x8d\xe4\xb9\x8913\n\x11\xe4\xba\xba\xe6\xb0\x91\xe7\x9a\x84\xe5\x90\x8d\xe4\xb9\x8914\n\x11\xe4\xba\xba\xe6\xb0\x91\xe7\x9a\x84\xe5\x90\x8d\xe4\xb9\x8915\n\x11\xe4\xba\xba\xe6\xb0\x91\xe7\x9a\x84\xe5\x90\x8d\xe4\xb9\x8916\n\x11\xe4\xba\xba\xe6\xb0\x91\xe7\x9a\x84\xe5\x90\x8d\xe4\xb9\x8917\n\x11\xe4\xba\xba\xe6\xb0\x91\xe7\x9a\x84\xe5\x90\x8d\xe4\xb9\x8918\n\x11\xe4\xba\xba\xe6\xb0\x91\xe7\x9a\x84\xe5\x90\x8d\xe4\xb9\x8919'
人民的名义1
人民的名义2
人民的名义3
人民的名义4
人民的名义5
人民的名义6
人民的名义7
人民的名义8
人民的名义9
人民的名义10
人民的名义11
人民的名义12
人民的名义13
人民的名义14
人民的名义15
人民的名义16
人民的名义17
人民的名义18
人民的名义19
example3_定义枚举类型
- 定义一个
DayOfWeek
枚举,包含七个值:MONDAY
、TUESDAY
、WEDNESDAY
、THURSDAY
、FRIDAY
、SATURDAY
、SUNDAY
。 - 创建一个
Schedule
消息,包含:- 星期几(
day
,枚举类型) - 活动描述(
activity
,字符串类型)
- 星期几(
- 枚举定义:使用
enum
关键字定义枚举类型,每个枚举常量都被分配一个整数值。 - 在消息中使用枚举:在 Protobuf 消息中可以使用定义好的枚举类型字段,从而提高代码的可读性和数据结构的清晰度。
- Python 使用:在 Python 中,枚举的值以整数表示,使用
Name()
方法可以将整数值转换为枚举常量的名称。
enum.proto
syntax = "proto3";
package study_enum;
enum DayOfWeek {
MONDAY = 0;
TUESDAY = 1;
WEDNESDAY = 2;
THURSDAY = 3;
FRIDAY = 4;
SATURDAY = 5;
SUNDAY = 6;
}
message Schedule {
DayOfWeek day = 1;
string activity = 2;
}
通过proto文件编译生成python文件
protoc --python_out=. enum.proto
main.py
import enum_pb2
test_enum = enum_pb2.Schedule()
test_enum.day = enum_pb2.DayOfWeek.MONDAY
test_enum.activity = "上学"
data = test_enum.SerializeToString()
print(data)
test_enum.ParseFromString(data)
print(test_enum.day)
print(enum_pb2.DayOfWeek.Name(test_enum.day))
print(test_enum.activity)
输出
b'\x12\x06\xe4\xb8\x8a\xe5\xad\xa6'
0
MONDAY
上学
example4_使用oneof
- 定义一个ContactInfo消息,包含以下字段:
phone_number
(字符串类型)email
(字符串类型)- 使用
oneof
来表示联系人信息字段,只能选择其中一个(电话号码或电子邮件)。
- 定义
oneof
:使用oneof
可以在同一消息中定义多个互斥字段,但任何时候只能设置其中一个字段。 - 存储空间节省:所有的
oneof
字段共享内存空间,因此只会有一个字段被存储。 - 互斥字段:设置一个
oneof
字段时,其他字段会被自动清除。 - 访问
oneof
字段:可以使用HasField
方法检查某个字段是否已经被设置。
oneof.proto
syntax = "proto3";
package study_oneOf;
message ContactInfo {
oneof contact {
string phone_number = 1;
string email = 2;
};
}
通过proto文件编译生成python文件
protoc --python_out=. oneof.proto
main.py
import oneof_pb2
contact = oneof_pb2.ContactInfo()
contact.email = "123@qq.com"
# 使用HasField检查字段是否设置
is_phone_number = contact.HasField("phone_number")
is_email = contact.HasField("email")
print(is_phone_number, is_email)
encode_data = contact.SerializeToString()
print(encode_data)
contact.ParseFromString(encode_data)
print(contact.phone_number)
print(contact.email)
输出
False True
b'\x12\n123@qq.com'
123@qq.com
example5_字段编号与保留
-
定义一个Person消息,包含以下字段:
- 姓名(
name
,字符串类型) - 年龄(
age
,整数类型) - 使用
reserved
来保留字段编号 2 和 3,避免将来重复使用。
- 姓名(
-
reserved
关键字用于保留 字段编号 和 字段名称,防止它们在未来被重用。 -
它对于处理字段删除、重命名以及防止版本间字段冲突非常有用。
-
reserved
语法可以指定字段编号和字段名称,避免不必要的冲突。
person.proto
syntax = "proto3";
package person;
message Person {
string name = 1;
int32 age = 2;
reserved 3, 4;
}
通过proto文件编译生成python文件
protoc --python_out=. person.proto
main.py
import Person_pb2
person = Person_pb2.Person()
person.name = "zhangsan"
person.age = 18
encode_data = person.SerializeToString()
print(encode_data)
new_person = Person_pb2.Person()
new_person.ParseFromString(encode_data)
print(new_person.name)
print(new_person.age)
输出
b'\n\x08zhangsan\x10\x12'
zhangsan
18
syntax = "proto3";
package person;
message Person {
string name = 1;
int32 age = 2;
reserved 3, 4;
}
通过proto文件编译生成python文件
protoc --python_out=. person.proto
main.py
import Person_pb2
person = Person_pb2.Person()
person.name = "zhangsan"
person.age = 18
encode_data = person.SerializeToString()
print(encode_data)
new_person = Person_pb2.Person()
new_person.ParseFromString(encode_data)
print(new_person.name)
print(new_person.age)
输出
b'\n\x08zhangsan\x10\x12'
zhangsan
18
标签:
相关文章
最新发布
- 光流法结合深度学习神经网络的原理及应用(完整代码都有Python opencv)
- Python 图像处理进阶:特征提取与图像分类
- 大数据可视化分析-基于python的电影数据分析及可视化系统_9532dr50
- 【Python】入门(运算、输出、数据类型)
- 【Python】第一弹---解锁编程新世界:深入理解计算机基础与Python入门指南
- 华为OD机试E卷 --第k个排列 --24年OD统一考试(Java & JS & Python & C & C++)
- Python已安装包在import时报错未找到的解决方法
- 【Python】自动化神器PyAutoGUI —告别手动操作,一键模拟鼠标键盘,玩转微信及各种软件自动化
- Pycharm连接SQL Sever(详细教程)
- Python编程练习题及解析(49题)
点击排行
- 版本匹配指南:Numpy版本和Python版本的对应关系
- 版本匹配指南:PyTorch版本、torchvision 版本和Python版本的对应关系
- Python 可视化 web 神器:streamlit、Gradio、dash、nicegui;低代码 Python Web 框架:PyWebIO
- 相关性分析——Pearson相关系数+热力图(附data和Python完整代码)
- Anaconda版本和Python版本对应关系(持续更新...)
- Python与PyTorch的版本对应
- Windows上安装 Python 环境并配置环境变量 (超详细教程)
- Python pyinstaller打包exe最完整教程