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/

posted on 2020-09-02 17:06  mlllily  阅读(196)  评论(0)    收藏  举报