python中有某些对象可以包装对底层内存阵列或缓冲区的访问。这样的对象包括内置字节和字节数组,还包括一些扩展名,例如array.array。出于特殊目的,例如数值计算,仿真或图像处理,第三方库可以定义自己的类型。

在这里,我们将介绍如何使用协议缓冲区,并显示:

  • 邮件中使用的不同消息格式。原始文件。
  • 如何使用协议缓冲区编译器。
  • 如何使用python协议缓冲区API写入和读取消息。

 

定义您的.proto文件格式

要创建联系人保存应用程序,您将需要以.proto文件开头。该文件将包含定义。在.proto文件中,您将指定要社交的数据结构,然后在相应的字段中添加名称和数据类型。

package tutorial;


message Person {

    required string name = 1;

    required int32 id = 2;

    optional string email = 3;


    enum PhoneType {

        MOBILE = 0;

        HOME = 1;

        WORK = 2;

    }

    message PhoneNumber {

        required string number = 1;

        optional PhoneType type = 2[
            default = HOME];

    }

}

message PhoneNumber {
    required string number = 1;
    optional PhoneType type = 2[
        default = Home];
}

repeated PhoneNumber phones = 4,

    message AdrssBook {
        repeated Person people = 1;
    }

  

现在遍历此代码的每个部分及其作用。

我们将从包声明开始创建一个文件,该文件将帮助我们防止与其他项目的任何类型的命名冲突。包由分层文件目录结构确定。因此,如果您在中定义了任何包,proto文件,它不会对生成的代码产生影响。建议在协议缓冲区名称空间以及其他语言或非python语言中定义一个。

在下一个语句中,您具有消息定义。它只包含一组类型字段。有许多不同的数据类型可用,包括int32 ,float ,string ,bool 。

您还可以通过使用其他消息类型作为字段类型来为消息添加更多的结构。在上面的示例中,“个人”消息包含“电话号码”消息,而“地址簿”消息包含“个人”消息。您甚至可以定义嵌套在其他消息中的消息类型-如您所见,PhoneNumber类型是在Person内部定义的。如果希望某个字段具有一个预定义的值列表之一,也可以定义枚举类型-在这里您要指定电话号码可以是MOBILE ,HOME或WORK之一。

每个元素上的“ = 1”,“ = 2”标记标识该字段在二进制编码中使用的唯一“标记”。

 

每个字段必须包含以下三个修饰符之一:

  1. required :必须提供该字段的值,否则该消息将被视为“未初始化”。序列化未初始化的消息将引发异常。解析未初始化的消息将失败。除此之外,必填字段的行为与可选字段完全相同。

 

  1. 可选:可能会或可能不会设置该字段。如果未设置可选字段值,则使用默认值。对于简单类型,您可以指定自己的默认值,就像在示例中为电话号码类型所做的那样。否则,将使用系统默认值:数字类型为零,字符串为空字符串,布尔值为false。对于嵌入式消息,默认值始终是消息的“默认实例”或“原型”,没有设置任何字段。调用访问器以获取未显式设置的可选(或必填)字段的值始终会返回该字段的默认值。

 

  1. 重复:该字段可以重复任意次(包括零次)。重复值的顺序将保留在协议缓冲区中。将重复字段视为动态大小的数组。


编译协议缓冲区

创建.proto后,它需要类对地址簿进行读写操作。为此,您将在.proto上运行协议缓冲区编译器proctoc:

如果尚未安装编译器,请下载软件包并按照README中的说明进行操作。

现在运行编译器,指定源目录(应用程序的源代码所在的位置,如果不提供值则为当前目录),目标目录(希望生成的代码进入的位置,通常与$ SRC_DIR相同)),以及.proto的路径。在这种情况下,请使用:

protoc -I=$SRC_DIR --python_out=$DST_DIR $SRC_DIR/addressbook.proto

  

因为您需要Python类,所以使用--python_out选项;其他受支持的语言也提供了类似的选项。

这将在您指定的目标目录中生成addressbook_pb2.py 。


协议缓冲区API

在Java和C ++协议中,缓冲区代码是自动生成的。不幸的是,在python中,Python协议缓冲区编译器不会自动生成数据访问代码。而是生成了另一个文件,该文件具有自己的解释,您会这样解释。

 

class Person(message.Message):
    __metaclass__ = reflection.GeneratedProtocolMessageType


class PhoneNumber(message.Message):
    __metaclass__ = reflection.GeneratetlProtocolMessageType

DESCRIPTOR = _PERSON_PHONENUMBER
DESCRIPTOR = _PERSON

call AddressBook(message.Message):
    __metaclass__ = reflection.GeneratedProtocolMessageType

DESCRIPTOR = ADDRESSBOOK

 

_ _metaclass_ _ =反射。GeneratedProtocolMessageType是重要的声明,在其中创建用于创建类的模板。在加载时,GeneratedProtocolMessageType元类使用指定的描述符创建每种消息类型都需要使用的所有Python方法,并将它们添加到相关的类中。然后,您可以在代码中使用完全填充的类。

 

现在,您可以使用Person类,就像将Message基类的每个字段都定义为常规字段一样。您可以按照以下代码示例进行编写:

 

import addressbook_pb2

person = addressbook_pb2.Person()

person.id  = 1234

person.name = "John Doe"

person.email = "jdoe@example.com"

phone = person.phones. add()

phone.number = "555-1234"

phone.type = addressbook_pb2.Person.Home

 

扩展协议缓冲区

在您的第一个或多个实现发布之后的某个时候,您可能需要改进协议缓冲区的定义。如果要使用向前和向后兼容的新协议缓冲区,则在此新版本的缓冲区中,您必须遵循以下规则:

  • 您不能更改任何操作字段的标签号。
  • 您不能删除缓冲区中的任何现有字段或添加任何新字段。
  • 您可能可以删除可选或复制的字段。
  • 您可以添加新的可选字段或重复的字段,但是为此您必须创建新的标签号。

如果遵循这些规则,您会完成什么?

如果遵循这些规则,旧代码将愉快地读取新消息,而忽略任何新字段。对于旧代码,已删除的可选字段将仅具有其默认值,而删除的重复字段将为空。新代码还将透明地读取旧消息。但是,请记住,新的可选字段不会出现在旧消息中,因此您将需要明确检查是否已使用has_设置它们,或使用[default = value]在.proto文件中提供合理的默认值。标签编号之后。如果未为可选元素指定默认值,则使用特定于类型的默认值:对于字符串,默认值为空字符串。对于布尔值,默认值为false。对于数字类型,默认值为零。还要注意,如果您添加了一个新的重复字段,则新代码将无法告诉它是空的(由新代码)还是根本没有被设置(由旧代码),因为没有has_标志。