protobuf 学习
什么是protobuf?
Protobuf是Protocol Buffers的简称,它是Google公司开发的一种数据描述语言,用于描述一种轻便高效的结构化数据存储格式,并于2008年对外开源。
Protobuf可以用于结构化数据串行化,或者说序列化。
Protobuf中最基本的数据单元是message,是类似Go语言中结构体的存在。在message中可以嵌套message或其它的基础数据类型的成员。
序列化和反序列化
序列化指将对象变为字节流,反序列化指将字节流转化为对象。
写proto文件 —— addressbook.proto
# 开头是包的声明,为了防止在不同的工程中命名冲突。
# 在Python中,包通常由目录结构决定的,所以这个由你的.proto文件定义的包,在你生成你代码中是没有效果的。但是,你应该坚持声明这条语句,为了在protocol Buffers的命名空间中防止名子的冲突,就像其它非Python的语言那样。
package tutorial;
# 然后,就是你定义的Message。(理解为类)
# 一个Message是一个包含一组类型字段的集合。有许多简单的标准的数据类型可以用在类型字段中,包括bool,int32,float,double和string。
# 你也可以使用更加多的结构来定义你的Message,例如用其它Message类型当作类型字段。
# “=1”,“=2”标记每个元素的识别,作为二进制编码中字段的唯一的标签。
# 1-15比更高的数字少一个字节编码,所以,作为最优化的方案,可以对常用的和要重复使用的元素使用这些标签,把16或最高的数字留给不常用和可选择的元素。
# 每个重复的字段里的元素要求重新编码它的标签号码,所以重复的字段特别适合使用这种优化。
message Person { required string name = 1; required int32 id = 2; optional string email = 3;
# 枚举类型的定义采用C++ scoping规则,也就是枚举值是枚举类型的兄弟类型,而不是子类型,所以避免在同一个package定义重名的枚举字段。 enum PhoneType { MOBILE = 0; HOME = 1; WORK = 2; }
# 在Person中再定义一个message: PhoneNumber message PhoneNumber { required string number = 1; optional PhoneType type = 2 [default = HOME]; } repeated PhoneNumber phone = 4; } message AddressBook { repeated Person person = 1; }
每个字段一定要被以下的修饰语修饰:
- required:一定要提供一个值给这个字段,否则这条Message会被认为“没有初始化”。序列化一列没有初始化的Message会出现异常。 解析一条没有初始化的Message会失败。除此而外,这个required字段的行为更类似于一个optional字段。
- optional:这个字段可以设置也可以不设置 。如果一个可选字段没有设置值,会用缺省的值。简单来说,你可以指定自己的默认值,就像我们在例子中对phone number类型所做的。另外,系统会缺省这样做:0给整数类型,空串给字符串类型,false给布尔类型。对于嵌入的Message,缺省的值通常会是“默认实例”或“原型”,对那些没有设置字段的Message。调用存取器获得一个可选的(或要求)字段的值,那些通常什么明确给出值的字段总是返回该字段的默认值。
- repeated:这个字段会重复n次( n>=0 )。重复的值给按顺序保存在protocol buffer中。重复的字段会被认为是动态的数组。
编译ptoto文件:
- 首先确保安装了protobuf: $ brew install protobuf
- $ protoc --python_out=./ addressbook.proto
- 执行上面的命令,发现在当前目录下生成了一份 addressbook_pb2.py 文件
序列化
接下来用protocol buffer 类了。将用户信息转化为proto格式存入地址簿文件。
def PromptForAddress(person): person.id = 1 person.name = 'mc.meng' person.email = '1140889958@qq.com' phone_number = person.phones.add() phone_number.number = "185xxxx2445" phone_number.type = addressbook_pb2.Person.MOBILE phone_number = person.phones.add() phone_number.number = "8678889" phone_number.type = addressbook_pb2.Person.HOME def write_test():
# 生成AddressBook对象 address_book = addressbook_pb2.AddressBook() address_book_file = "./data/addressbook.txt"
# repeated类型的数据要赋值之前,先执行add()函数初始化 PromptForAddress(address_book.people.add()) f = open(address_book_file, "wb")
# 转pb # 序列化这个message和以字符串的方式返回。 注意,这是二进行字节,不是一个文本; 我们只使用str类型作为一个方便的容器。 msg = address_book.SerializeToString() f.write(msg) f.close() if __name__ == "__main__": write_test()
执行之后会发现在 ./data文件加下生成了一个新的文件 addressbook.txt, 里面有刚刚存入的信息

反序列化 (从文件中读取出来)
import protobuf.addressbook_pb2 as addressbook_pb2 def ListPeople(address_book): for person in address_book.people: print("Person ID:", person.id) print(" Name:", person.name) print(" E-mail address:", person.email) for phone_number in person.phones: if phone_number.type == addressbook_pb2.Person.MOBILE: print(" Mobile phone #: ") elif phone_number.type == addressbook_pb2.Person.HOME: print(" Home phone #: ") elif phone_number.type == addressbook_pb2.Person.WORK: print(" Work phone #: ") print(phone_number.number) def read_test():
# 生成AddressBook对象 address_book = addressbook_pb2.AddressBook() address_book_file = "./data/addressbook.txt" f = open(address_book_file, "rb") # 解析pb address_book.ParseFromString(f.read()) f.close()
# 输出 ListPeople(address_book) if __name__ == "__main__": read_test()
https://colobu.com/2019/10/03/protobuf-ultimate-tutorial-in-go/
猪猪侠要努力呀!
浙公网安备 33010602011771号