Google Protocol Buffers(简称 Protobuf)是一种由 Google 开发的数据序列化格式,用于定义结构化数据并在不同的系统或编程语言之间高效地传输和存储数据。它可以看作是一种更轻量且更高效的替代方案,类似于 XML 或 JSON,但在性能和数据大小上表现更优。其使用步骤如下:
- 安装 Protocol Buffers 编译器
- 编写 .proto 文件:定义数据结构
- 编译 .proto 文件:使用编译器将 .proto 文件编译为目标语言代码。
- 在代码中使用生成的文件:创建和操作数据对象,实现序列化和反序列化。
1. 安装编译器
GitHub:https://github.com/protocolbuffers/protobuf
Releases:https://github.com/protocolbuffers/protobuf/releases
├───bin │ protoc.exe │ └───include └───google └───protobuf │ any.proto │ api.proto │ cpp_features.proto │ descriptor.proto │ duration.proto │ empty.proto │ field_mask.proto │ java_features.proto │ source_context.proto │ struct.proto │ timestamp.proto │ type.proto │ wrappers.proto │ └───compiler plugin.proto
- bin 目录下的 protoc.exe 就是我们需要的编译器
- include 目录下是常用 Protobuf 数据类型或功能
接下来,将 bin 目录配置到 PATH 环境变量中,最后输入下面命令检查编译器是否可用:
protoc --version
2. 工具安装
安装 Python Protobuf 工具库:
pip install protobuf==5.28.3 -i https://pypi.tuna.tsinghua.edu.cn/simple/
注意:protobuf 编译器版本和 python protobuf 库的版本要保持一致。我们可以从 Protobuf 编译器生成的 Python 文件的顶部注释中查看对应的版本。例如:
# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: protos/developer.proto # Protobuf Python Version: 5.28.3 """Generated protocol buffer code."""
3. 数据定义
创建 demo.proto 文件,定义数据如下:
syntax = "proto3"; enum Week { Mon = 0; Tue = 1; Wed = 2; Thu = 3; Fri = 4; Sat = 5; Sun = 6; } message Custom { int32 attr1 = 1; int32 attr2 = 2; } message Demo { string name = 1; int32 numb = 2; Week week = 3; repeated int32 rept = 4; map<string, int32> mpsi = 5; Custom cust = 6; }
对于 Python,每个 proto 文件会生成两种不同的文件:
- foo_pb2.py: 实际使用的模块文件
- foo_pb2.pyi:Python 的
.pyi
文件是 “Python Interface” 文件,用于提供静态类型提示信息
需要注意的是,默认 pyi 文件不会自动生成需要指定,需要指定 --python_out=pyi_out:输出路径
。
# 不生成 pyi 文件 protoc *.proto --python_out=. # 生成 pyi 文件 protoc *.proto --python_out=pyi_out:.
4. 数据操作
在 Protobuf 中 SerializeToString 函数可以将 Python 数据对象序列化为二进制数据(字节类型),ParseFromString 函数可用以将二进制数据反序列化为 Python 数据对象。
from demo_pb2 import Demo, Week, Custom from google.protobuf import json_format # 1. 对象操作 def test01(): demo = Demo() # 1. 基本类型 demo.name = "张三" demo.numb = 200 demo.week = Week.Thu # 2. 自定义类型 demo.cust.attr1 = 100 demo.cust.attr2 = 200 # 3. repeated 类型 demo.rept.append(10) demo.rept.append(20) demo.rept.insert(1, 30) # 4. map 类型 demo.mpsi['k1'] = 100 demo.mpsi['k2'] = 200 demo.mpsi['k3'] = 300 print(demo) # 5. 其他方法 print('对象大小:', demo.ByteSize()) # demo = Demo(name="张三", # numb=100, # week=Week.Thu, # rept=[10, 20, 30], # mpsi={'v1': 100, 'v2': 200}, # cust=Custom(attr1=11, attr2=22)) # 2. 对象序列化 def test02(): demo = Demo(name='张三', numb=100, week=Week.Thu, rept=[10, 20], mpsi={'v1': 100, 'v2': 200}, cust=Custom(attr1=11, attr2=22)) # 1. 序列化为二进制数据 data = demo.SerializeToString() print(data) open('data.bin', 'wb').write(data) # 2. 序列化为 json 格式 json = json_format.MessageToJson(demo) print(json) open('data.json', 'w').write(json) # 3. 对象反序列化 def test03(): # 1. 反序列化二进制数据 demo = Demo() data = open('data.bin', 'rb').read() demo.ParseFromString(data) print(demo) print('-' * 30) # 反序列化 json 数据 data = open('data.json', 'r').read() demo = json_format.Parse(data, Demo()) print(demo) if __name__ == '__main__': test01() test02() test03()