Protocol Buffers是一种轻量级的数据交换格式,用于结构化数据的序列化和反序列化,它可以更高效的传输和存储数据。

通常用于直播平台和消息通信之类的场景。

Protobuf介绍

安装python关于protobuf相关环境

首先新建一个python环境,这里不展开了,之后使用以下命令安装protobuf相关依赖

pip install protobuf
pip install google
pip install google-cloud
pip install google-cloud-vision

安装protocol buffers编译器

protocol buffers的github官网
protobuf编译器是用c++实现的,如果不是使用c++的用户,安装protocol编译器最简单的方法是安装一个二进制的预编译包

将下载下来的zip包中的可执行exe文件protoc.exe移动至新建的python项目下。

protobuf的使用

protobuf数据结构介绍

protobuf相当于一个数据结构,包含了stirng、int等等数据结构类型。

接下来介绍如何编写proto文件。
首先新建一个.proto后缀的文件为person.proto:

syntax = "proto3"; // 版本号3
// 定义一个Person的结构体(数据结构)
message Person {
  string name = 1; // 定义一个string类型的字段,字段名字为name, 序号为1
  int32 age = 2; // 定义一个int32类型的字段,字段名字为age, 序号为2
  Gender gender = 3; // 定义一个下面创建的Gender枚举类型的字段,字段名字为gender,序号为3
  map<string, string> email = 4;// map类型,字段名字为email序号为4

  repeated Cars car = 5; // 重复列表,定义一个Cars类型
  bool is_student = 6; // 是否是学生
  bytes hi = 7; // 定义一个bytes类型,解析出来是base64加密
}

// 定义一个Gender枚举类型,后面可以给Person中的gender赋值
enum Gender {
  UNKNOWN = 0;
  MALE = 1;
  FEMALE = 2;
  OTHER =3;
}

// 定义一个Car的结构体(数据结构)
message Cars {
  string make = 1; // 产地
  int32 year = 2;
}

proto文件的编译

下面对这些结构体进行一个序列化和反序列化的实践,第一步就是需要使用protoc.exe对proto文件进行编译:
protoc --python_out=. person.proto

  • --python_out=.指的是以python的格式输出
  • person.proto指定编译的文件

运行之后生成person_pb2.py文件:

# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler.  DO NOT EDIT!
# source: person.proto
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
# @@protoc_insertion_point(imports)

_sym_db = _symbol_database.Default()




DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0cperson.proto\"\xc2\x01\n\x06Person\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0b\n\x03\x61ge\x18\x02 \x01(\x05\x12\x17\n\x06gender\x18\x03 \x01(\x0e\x32\x07.Gender\x12!\n\x05\x65mail\x18\x04 \x03(\x0b\x32\x12.Person.EmailEntry\x12\x13\n\x04\x63\x61rs\x18\x05 \x03(\x0b\x32\x05.Cars\x12\x12\n\nis_student\x18\x06 \x01(\x08\x12\n\n\x02hi\x18\x07 \x01(\x0c\x1a,\n\nEmailEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\"\n\x04\x43\x61rs\x12\x0c\n\x04make\x18\x01 \x01(\t\x12\x0c\n\x04year\x18\x02 \x01(\x05*6\n\x06Gender\x12\x0b\n\x07UNKUNOW\x10\x00\x12\x08\n\x04MALE\x10\x01\x12\n\n\x06\x46\x45MALE\x10\x02\x12\t\n\x05OTHER\x10\x03\x62\x06proto3')

_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'person_pb2', _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
  DESCRIPTOR._options = None
  _PERSON_EMAILENTRY._options = None
  _PERSON_EMAILENTRY._serialized_options = b'8\001'
  _globals['_GENDER']._serialized_start=249
  _globals['_GENDER']._serialized_end=303
  _globals['_PERSON']._serialized_start=17
  _globals['_PERSON']._serialized_end=211
  _globals['_PERSON_EMAILENTRY']._serialized_start=167
  _globals['_PERSON_EMAILENTRY']._serialized_end=211
  _globals['_CARS']._serialized_start=213
  _globals['_CARS']._serialized_end=247
# @@protoc_insertion_point(module_scope)

protoc的序列化与反序列化

接下来介绍如何使用protoc进行序列化和反序列化。

序列化

新建一个main.py文件:

import person_pb2


def serialize_person():
    # 创建一个Person对象
    person = person_pb2.Person() # 实例化对象
    person.name = '张三'
    person.age = 18
    person.gender = person_pb2.Gender.MALE
    # map类型,键值对
    person.email['qq'] = 'qiulianmao@qq.com'
    person.email['vx'] = 'qiulianmao@vx.com'

    # 列表类型
    car1 = person.cars.add()
    car1.make = "上海"
    car1.year = 12

    car2 = person.cars.add()
    car2.make = "北极"
    car2.year = 3

    person.is_student = True
    person.hi = b'hello world' # bytes类型
    print(person)
    print("=======================================")
    serialized_data = person.SerializeToString()
    print(serialized_data)
    return serialized_data

serialize_person()
  • 第24行将新建的person进行字符串序列化

执行结果:

name: "张三"
age: 18
gender: MALE
email {
key: "vx"
value: "qiulianmao@vx.com"
}
email {
key: "qq"
value: "qiulianmao@qq.com"
}
cars {
make: "上海"
year: 12
}
cars {
make: "北极"
year: 3
}
is_student: true
hi: "hello world"

=======================================
b'\n\x06\xe5\xbc\xa0\xe4\xb8\x89\x10\x12\x18\x01"\x17\n\x02vx\x12\x11qiulianmao@vx.com"\x17\n\x02qq\x12\x11qiulianmao@qq.com*\n\n\x06\xe4\xb8\x8a\xe6\xb5\xb7\x10\x0c*\n\n\x06\xe5\x8c\x97\xe6\x9e\x81\x10\x030\x01:\x0bhello world'

可以看到打印出来的序列化之后的字符串是没办法用肉眼直接还原成原始数据的,已经起到了一定程度上的加密作用。

反序列化

接下来进行反序列化,代码如下:

import person_pb2
from google.protobuf.json_format import MessageToDict

def serialize_person():
    # 创建一个Person对象
    person = person_pb2.Person()  # 实例化对象
    person.name = '张三'
    person.age = 18
    person.gender = person_pb2.Gender.MALE
    # map类型,键值对
    person.email['qq'] = 'qiulianmao@qq.com'
    person.email['vx'] = 'qiulianmao@vx.com'

    # 列表类型
    car1 = person.cars.add()
    car1.make = "上海"
    car1.year = 12

    car2 = person.cars.add()
    car2.make = "北极"
    car2.year = 3

    person.is_student = True
    person.hi = b'hello world'
    # print(person)
    # print("=======================================")
    serialized_data = person.SerializeToString()
    print(serialized_data)
    print("=======================================")
    return serialized_data


def deserialize_person(data):
    person = person_pb2.Person()  # 还是创建一个person实例
    person.ParseFromString(data)  # 反序列化

    # 将person实例转换成python字典并打印
    dict = MessageToDict(person)
    print(dict)


data = serialize_person()
deserialize_person(data)
  • 第32行使用ParseFromString方法将字符串反序列化为protoc的person对象
  • 第35行使用google.protobuf.json_format中的MessageToDict将protoc的实例转换成python字典

运行结果如下:

b'\n\x06\xe5\xbc\xa0\xe4\xb8\x89\x10\x12\x18\x01"\x17\n\x02vx\x12\x11qiulianmao@vx.com"\x17\n\x02qq\x12\x11qiulianmao@qq.com*\n\n\x06\xe4\xb8\x8a\xe6\xb5\xb7\x10\x0c*\n\n\x06\xe5\x8c\x97\xe6\x9e\x81\x10\x030\x01:\x0bhello world'
=======================================
{'name': '张三', 'age': 18, 'gender': 'MALE', 'email': {'qq': 'qiulianmao@qq.com', 'vx': 'qiulianmao@vx.com'}, 'cars': [{'make': '上海', 'year': 12}, {'make': '北极', 'year': 3}], 'isStudent': True, 'hi': 'aGVsbG8gd29ybGQ='}

格式化一下反序列化出来的python字典的结果:

{
    'name': '张三'
    , 'age': 18
    , 'gender': 'MALE'
    , 'email':
    {
        'qq': 'qiulianmao@qq.com'
        , 'vx': 'qiulianmao@vx.com'
    }
    , 'cars': [
    {
        'make': '上海'
        , 'year': 12
    }
    , {
        'make': '北极'
        , 'year': 3
    }]
    , 'isStudent': True
    , 'hi': 'aGVsbG8gd29ybGQ='
}

可以看到之前的person实例化对象已经转换成python中的字典,后面可以通过python灵活调用其中的属性。
这里的hi是base64编码之后的内容,使用base64解码:
image.png
成功解码出结果。

以上就是protobuf的大致介绍以及使用流程。

最后修改:2024 年 03 月 13 日
如果觉得我的文章对你有用,请随意赞赏