Python基础(①⑥Protobuf)

发布于:2025-09-08 ⋅ 阅读:(20) ⋅ 点赞:(0)

 Protobuf 理解成一种「高效的结构化数据格式」,专门用来在不同程序之间传递或存储数据。它和 JSON、XML 的作用类似,但更小巧、更快。

为什么需要 Protobuf?

假设你要在两个程序之间传递一个「用户信息」,包含姓名、年龄、邮箱。

用 JSON 可能是这样:

{"name":"小明","age":18,"email":"xiaoming@test.com"}

用 Protobuf 存储同样的信息,会变成二进制数据(类似 \x08\x12\x06\xe5\xb0\x8f\xe6\x98\x8e...),虽然人看不懂,但程序能高效处理,而且体积更小。

Protobuf 的核心:.proto 文件

Protobuf 不直接写数据,而是先定义「数据结构规则」,这个规则就写在 .proto 文件里。
比如上面的用户信息,规则文件(user.proto)长这样:

syntax = "proto3";  // 声明使用第3版语法(最新版)

// 定义一个"用户"数据结构
message User {
  string name = 1;   // 姓名(string类型)
  int32 age = 2;     // 年龄(整数类型)
  string email = 3;  // 邮箱(string类型)
}

这里的 1、2、3 是「字段编号」,不是值!作用是:

在二进制编码时标识字段(比用字段名更省空间)
后续添加新字段时,老程序仍能兼容(只要不删除 / 修改已有编号)

特点 JSON Protobuf
格式 文本(人能看懂) 二进制(人看不懂)
体积 较大(包含字段名等) 小(用编号代替字段名)
速度 解析较慢 解析极快
类型检查 弱(运行时可能出错) 强(编译时就报错)
兼容性 需手动处理版本兼容 天然支持向后兼容

程序 A 要给程序 B 发送一个 "用户信息"(姓名、年龄、邮箱)
序列化:程序 A 把这些信息按 Protobuf 规则转换成二进制数据(像打包好的箱子)
传输:二进制数据通过网络发给程序 B(像快递运输)
反序列化:程序 B 收到二进制数据,按同样的规则还原成 "姓名、年龄、邮箱"(像拆箱取东西)

安装 protoc 编译器

浏览器直达 GitHub Releases:

Releases · protocolbuffers/protobuf · GitHub

选 protoc-28.2-win64.zip

D:\protoc-28.2\bin 追加到系统 PATH

cmd输入

protoc --version

然后安装protobuf库

pip install protobuf

创建 Protobuf tobuf 规则

在任意文件夹(比如 D:\protobuf-test)中,新建一个 student.proto 文件,用记事本或 VS Code 打开,写入以下内容:

syntax = "proto3";  // 使用第 3 版语法(必须写在第一行)

// 定义一个「学生」数据结构
message Student {
  string name = 1;   // 姓名(编号 1)
  int32 age = 2;     // 年龄(编号 2)
  string school = 3; // 学校(编号 3)
  float score = 4;   // 分数(编号 4)
}

message Student 相当于 Python 中的 class Student,用来定义数据结构
每个字段格式:类型 字段名 = 编号(编号是 1-15 的数字,不能重复,用于二进制编码)

生成 Python 代码(自动工具)

打开命令提示符(Win+R 输入 cmd),进入上面的文件夹:

cd D:\protobuf-test

输入以下命令,用编译器把 .proto 文件转成 Python 代码:

protoc --python_out=. student.proto

执行后,文件夹里会多出一个 student_pb2.py 文件,这个文件是自动生成的,不用修改,里面包含了操作 Student 数据的所有工具。

编写 Python 代码使用 Protobuf

在同一文件夹中,新建 main.py 文件,写入以下代码:

import student_pb2  # 导入自动生成的代码

def main():
    # 1. 创建一个学生对象并赋值
    student = student_pb2.Student()
    student.name = "小明"
    student.age = 15
    student.school = "阳光中学"
    student.score = 95.5

    print("原始数据:")
    print(f"姓名:{student.name}")
    print(f"年龄:{student.age}")
    print(f"学校:{student.school}")
    print(f"分数:{student.score}\n")

    # 2. 序列化:把对象转成二进制(可用于网络传输或存储)
    serialized_data = student.SerializeToString()
    print(f"序列化后的二进制长度:{len(serialized_data)} 字节")
    print(f"二进制数据(十六进制):{serialized_data.hex()}\n")

    # 3. 反序列化:把二进制转回对象
    new_student = student_pb2.Student()
    new_student.ParseFromString(serialized_data)

    print("反序列化后的数据:")
    print(f"姓名:{new_student.name}")
    print(f"年龄:{new_student.age}")
    print(f"学校:{new_student.school}")
    print(f"分数:{new_student.score}")

if __name__ == "__main__":
    main()

对比维度 XML Protobuf JSON
数据格式 标签式文本格式(如 <name>小明</name> 二进制格式(不可读,需工具解析) 键值对文本格式(如 {"name": "小明"}
可读性 较高(文本格式,结构清晰) 极低(二进制,人类无法直接阅读) 高(文本格式,结构简洁)
序列化 / 反序列化速度 较慢(解析标签开销大) 极快(二进制编码,解析效率高) 较快(文本解析,效率低于 Protobuf)
数据体积 较大(标签冗余多) 极小(二进制压缩,无冗余) 中等(键名重复,比 XML 紧凑)
类型安全 弱(需额外定义 Schema 校验) 强(依赖 .proto 文件严格定义类型) 弱(默认无类型,需额外校验)
扩展性 较好(可通过命名空间、标签扩展) 极佳(支持字段增删,兼容旧版本) 较好(键值对灵活,但无官方扩展机制)
跨语言支持 广泛支持(几乎所有语言) 广泛支持(官方提供多语言生成工具) 广泛支持(所有主流语言内置 / 库支持)
Schema 定义 可选(需通过 XSD 单独定义) 必须(通过 .proto 文件强制定义) 可选(可通过 JSON Schema 定义)
适用场景 配置文件、文档标记(如 HTML 基于 XML) 高性能通信(如 RPC、游戏协议) 前后端交互、API 接口(如 RESTful)
学习成本 中等(需学习标签语法和 XSD) 中等(需学习 .proto 语法) 低(语法简单直观)

123


网站公告

今日签到

点亮在社区的每一天
去签到