1. 程式人生 > >ProtoBuf開發者指南大全(Google Protocol Buffer協議)

ProtoBuf開發者指南大全(Google Protocol Buffer協議)

目錄

  • 1   概覽
    • 1.1   什麼是protocol buffer
    • 1.2   他們如何工作
    • 1.3   為什麼不用XML?
    • 1.4   聽起來像是為我的解決方案,如何開始?
    • 1.5   一點歷史
  • 2   語言指導
    • 2.1   定義一個訊息型別
    • 2.2   值型別
    • 2.3   可選欄位與預設值
    • 2.4   列舉
    • 2.5   使用其他訊息型別
    • 2.6   巢狀型別
    • 2.7   更新一個數據型別
    • 2.8   擴充套件
    • 2.9   包
    • 2.10   定義服務
    • 2.11   選項
    • 2.12   生成你的類
  • 3   程式碼風格指導
    • 3.1   訊息與欄位名
    • 3.2   列舉
    • 3.3   服務
  • 4   編碼
    • 4.1   一個簡單的訊息
    • 4.2   基於128的Varints
    • 4.3   訊息結構
    • 4.4   更多的值型別
    • 4.5   內嵌訊息
    • 4.6   可選的和重複的元素
    • 4.7   欄位順序
  • 5   ProtocolBuffer基礎:C++
  • 6 ProtocolBuffer基礎:Java
  • 7   ProtocolBuffer基礎:Python
    • 7.1   為什麼使用ProtocolBuffer?
    • 7.2   哪裡可以找到例子程式碼
    • 7.3   定義你的協議格式
    • 7.4   編譯你的ProtocolBuffer
    • 7.5   ProtocolBuffer API
      • 7.5.1   列舉
      • 7.5.2   標準訊息方法
      • 7.5.3   解析與序列化
    • 7.6   寫訊息
    • 7.7   讀訊息
    • 7.8   擴充套件ProtocolBuffer
    • 7.9   高階使用
  • 8   參考概覽
  • 9   C++程式碼生成
  • 10   C++ API
  • 11   Java程式碼生成
  • 12   Java API
  • 13   Python程式碼生成
    • 13.1   編譯器的使用
    • 13.2   包
    • 13.3   訊息
    • 13.4   欄位
      • 13.4.1   簡單欄位
      • 13.4.2   簡單訊息欄位
      • 13.4.3   重複欄位
      • 13.4.4   重複訊息欄位
      • 13.4.5   列舉型別
      • 13.4.6   擴充套件
    • 13.5   服務
      • 13.5.1   介面
      • 13.5.2   存根(Stub)
  • 14   Python API
  • 15   其他語言

1   概覽

歡迎來到protocol buffer的開發者指南文件,一種語言無關、平臺無關、擴充套件性好的用於通訊協議、資料儲存的結構化資料序列化方法。

本文件面向希望使用protocol buffer的Java、C++或Python開發者。這個概覽介紹了protocol buffer,並告訴你如何開始,你隨後可以跟隨程式設計指導( http://code.google.com/apis/protocolbuffers/docs/tutorials.html )深入瞭解protocol buffer編碼方式( http://code.google.com/apis/protocolbuffers/docs/encoding.html )。API參考文件( http://code.google.com/apis/protocolbuffers/docs/reference/overview.html )同樣也是提供了這三種程式語言的版本,不夠協議語言( http://code.google.com/apis/protocolbuffers/docs/proto.html )和樣式( http://code.google.com/apis/protocolbuffers/docs/style.html )指導都是編寫 .proto 檔案。

1.1   什麼是protocol buffer

ProtocolBuffer是用於結構化資料序列化的靈活、高效、自動的方法,有如XML,不過它更小、更快、也更簡單。你可以定義自己的資料結構,然後使用程式碼生成器生成的程式碼來讀寫這個資料結構。你甚至可以在無需重新部署程式的情況下更新資料結構。

1.2   他們如何工作

你首先需要在一個 .proto 檔案中定義你需要做序列化的資料結構資訊。每個ProtocolBuffer資訊是一小段邏輯記錄,包含一系列的鍵值對。這裡有個非常簡單的 .proto 檔案定義了個人資訊:

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];
    }

    repeated PhoneNumber phone=4;
}

有如你所見,訊息格式很簡單,每個訊息型別擁有一個或多個特定的數字欄位,每個欄位擁有一個名字和一個值型別。值型別可以是數字(整數或浮點)、布林型、字串、原始位元組或者其他ProtocolBuffer型別,還允許資料結構的分級。你可以指定可選欄位,必選欄位和重複欄位。你可以在( http://code.google.com/apis/protocolbuffers/docs/proto.html )找到更多關於如何編寫 .proto 檔案的資訊。

一旦你定義了自己的報文格式(message),你就可以執行ProtocolBuffer編譯器,將你的 .proto 檔案編譯成特定語言的類。這些類提供了簡單的方法訪問每個欄位(像是 query() 和 set_query() ),像是訪問類的方法一樣將結構序列化或反序列化。例如你可以選擇C++語言,執行編譯如上的協議檔案生成類叫做 Person 。隨後你就可以在應用中使用這個類來序列化的讀取報文資訊。你可以這麼寫程式碼:

Person person;
person.set_name("John Doe");
person.set_id(1234);
person.set_email("[email protected]");
fstream.output("myfile",ios::out | ios::binary);
person.SerializeToOstream(&output);

然後,你可以讀取報文中的資料:

fstream input("myfile",ios::in | ios:binary);
Person person;
person.ParseFromIstream(&input);
cout << "Name: " << person.name() << endl;
cout << "E-mail: " << person.email() << endl;

你可以在不影響向後相容的情況下隨意給資料結構增加欄位,舊有的資料會忽略新的欄位。所以如果使用ProtocolBuffer作為通訊協議,你可以無須擔心破壞現有程式碼的情況下擴充套件協議。

你可以在API參考( http://code.google.com/apis/protocolbuffers/docs/reference/overview.html )中找到完整的參考,而關於ProtocolBuffer的報文格式編碼則可以在( http://code.google.com/apis/protocolbuffers/docs/encoding.html )中找到。

1.3   為什麼不用XML?

ProtocolBuffer擁有多項比XML更高階的序列化結構資料的特性,ProtocolBuffer:

  • 更簡單
  • 小3-10倍
  • 快20-100倍
  • 更少的歧義
  • 可以方便的生成資料存取類

例如,讓我們看看如何在XML中建模Person的name和email欄位:

<person>
    <name>John Doe</name>
    <email>[email protected]</email>
</person>

對應的ProtocolBuffer報文則如下:

#ProtocolBuffer的文字表示
#這不是正常時使用的二進位制資料
person {
    name: "John Doe"
    email: "[email protected]"
}

當這個報文編碼到ProtocolBuffer的二進位制格式( http://code.google.com/apis/protocolbuffers/docs/encoding.html )時(上面的文字僅用於除錯和編輯),它只需要28位元組和100-200ns的解析時間。而XML的版本需要69位元組(除去空白)和 5000-10000ns的解析時間。

當然,操作ProtocolBuffer也很簡單:

cout << "Name: " << person.name() << endl;
cout << "E-mail: " << person.email() << endl;

而XML的你需要:

cout << "Name: "
     << person.getElementsByTagName("name")->item(0)->innerText()
     << endl;
cout << "E-mail: "
     << person.getElementsByTagName("email")->item(0)->innerText()
     << end;

當然,ProtocolBuffer並不是在任何時候都比XML更合適,例如ProtocolBuffer無法對一個基於標記文字的文件建模,因為你根本沒法方便的在文字中插入結構。另外,XML是便於人類閱讀和編輯的,而ProtocolBuffer則不是。還有XML是自解釋的,而 ProtocolBuffer僅在你擁有報文格式定義的 .proto 檔案時才有意義。

1.4   聽起來像是為我的解決方案,如何開始?

下載包( http://code.google.com/p/protobuf/downloads/ ),包含了Java、Python、C++的ProtocolBuffer編譯器,用於生成你需要的IO類。構建和安裝你的編譯器,跟隨README的指令就可以做到。

一旦你安裝好了,就可以跟著程式設計指導( http://code.google.com/apis/protocolbuffers/docs/tutorials.html )來選擇語言-隨後就是使用ProtocolBuffer建立一個簡單的應用了。

1.5   一點歷史

ProtocolBuffer最初是在Google開發的,用以解決索引伺服器的請求、響應協議。在使用ProtocolBuffer之前,有一種格式用以處理請求和響應資料的編碼和解碼,並且支援多種版本的協議。而這最終導致了醜陋的程式碼,有如:

if (version==3) {
    ...
}else if (version>4) {
    if (version==5) {
        ...
    }
    ...
}

通訊協議因此變得越來越複雜,因為開發者必須確保,發出請求的人和接受請求的人必須同時相容,並且在一方開始使用新協議時,另外一方也要可以接受。

ProtocolBuffer設計用於解決這一類問題:

  • 很方便引入新欄位,而中間伺服器可以忽略這些欄位,直接傳遞過去而無需理解所有的欄位。
  • 格式可以自描述,並且可以在多種語言中使用(C++、Java等)

然而使用者仍然需要手寫解析程式碼。

隨著系統的演化,他需要一些其他的功能:

  • 自動生成編碼和解碼程式碼,而無需自己編寫解析器。
  • 除了用於簡短的RPC(Remote Procedure Call)請求,人們使用ProtocolBuffer來做資料儲存格式(例如BitTable)。
  • RPC伺服器介面可以作為 .proto 檔案來描述,而通過ProtocolBuffer的編譯器生成存根(stub)類供使用者實現伺服器介面。

ProtocolBuffer現在已經是Google的混合語言資料標準了,現在已經正在使用的有超過48,162種報文格式定義和超過 12,183個 .proto 檔案。他們用於RPC系統和持續資料儲存系統。

2   語言指導

本指導描述瞭如何使用ProtocolBuffer語言來定義結構化資料型別,包括 .proto 檔案的語法和如何生成存取類。

這是一份指導手冊,一步步的例子使用文件中的多種功能,檢視入門指導( http://code.google.com/apis/protocolbuffers/docs/tutorials.html )選擇你的語言。

2.1   定義一個訊息型別

@waiting …

2.2   值型別

@waiting …

2.3   可選欄位與預設值

@waiting …

2.4   列舉

@waiting …

2.5   使用其他訊息型別

@waiting …

2.6   巢狀型別

@waiting …

2.7   更新一個數據型別

@waiting …

2.8   擴充套件

@waiting …

2.9   包

@waiting …

2.10   定義服務

@waiting …

2.11   選項

@waiting …

2.12   生成你的類

@waiting …

3   程式碼風格指導

本文件提供了 .proto 檔案的程式碼風格指導。按照慣例,你將會,你將會生成一些便於閱讀和一致的ProtocolBuffer定義檔案。

3.1   訊息與欄位名

使用駱駝風格的大小寫命名,即單詞首字母大寫,來做訊息名。使用GNU的全部小寫,使用下劃線分隔的方式定義欄位名:

message SongServerRequest {
    required string song_name=1;
}

使用這種命名方式得到的名字如下:

C++:
    const string& song_name() {...}
    void set_song_name(const string& x) {...}

Java:
    public String getSongName() {...}
    public Builder setSongName(String v) {...}

3.2   列舉

使用駱駝風格做列舉名,而用全部大寫做值的名字:

enum Foo {
    FIRST_VALUE=1;
    SECOND_VALUE=2;
}

每個列舉值最後以分號結尾,而不是逗號。

3.3   服務

如果你的 .proto 檔案定義了RPC服務,你可以使用駱駝風格:

service FooService {
    rpc GetSomething(FooRequest) returns (FooResponse);
}

4   編碼

本文件描述了ProtocolBuffer的序列化二進位制資料格式定義。你如果僅僅是在應用中使用ProtocolBuffer,並不需要知道這些,但是這些會對你定義高效的格式有所幫助。

4.1   一個簡單的訊息

@waiting …

4.2   基於128的Varints

@waiting …

4.3   訊息結構

@waiting …

4.4   更多的值型別

@waiting …

4.5   內嵌訊息

@waiting …

4.6   可選的和重複的元素

@waiting …

4.7   欄位順序

@waiting …

5   ProtocolBuffer基礎:C++

@waiting …

6   ProtocolBuffer基礎:Java

@waiting …

7   ProtocolBuffer基礎:Python

本指南給Python程式設計師一個快速使用的ProtocolBuffer的指導。通過一些簡單的例子來在應用中使用ProtocolBuffer,它向你展示瞭如何:

  • 定義 .proto 訊息格式檔案
  • 使用ProtocolBuffer編譯器
  • 使用Python的ProtocolBuffer程式設計介面來讀寫訊息

這並不是一個在Python中使用ProtocolBuffer的完整指導。更多細節請參考手冊資訊,檢視語言指導( http://code.google.com/apis/protocolbuffers/docs/proto.html ),Python API( http://code.google.com/apis/protocolbuffers/docs/reference/python/index.html ),和編碼手冊( http://code.google.com/apis/protocolbuffers/docs/encoding.html )。

7.1   為什麼使用ProtocolBuffer?

下面的例子”地址本”應用用於讀寫人的聯絡資訊。每個人有name、ID、email,和聯絡人電話號碼。

如何序列化和讀取結構化資料呢?有如下幾種問題:

  • 使用Python的pickle,這是語言內建的預設方法,不過沒法演化,也無法讓其他語言支援。
  • 你可以發明一種資料編碼方法,例如4個整數”12:3-23:67″,這是簡單而靈活的方法,不過你需要自己寫解析器程式碼,且只適用於簡單的資料。
  • 序列化資料到XML。這種方法因為可讀性和多種語言的相容函式庫而顯得比較吸引人,不過這也不是最好的方法,因為XML浪費空間是臭名昭著的,編碼解碼也很浪費時間。而XML DOM樹也是很複雜的。

ProtocolBuffer提供了靈活、高效、自動化的方法來解決這些問題。通過ProtocolBuffer,只需要寫一個 .proto 資料結構描述檔案,就可以編譯到幾種語言的自動編碼解碼類。生成的類提供了setter和getter方法來控制讀寫細節。最重要的是 ProtocolBuffer支援後期擴充套件協議,而又確保舊格式可以相容。

7.2   哪裡可以找到例子程式碼

原始碼發行包中已經包含了,在”example”資料夾。

7.3   定義你的協議格式

想要建立你的地址本應用,需要開始於一個 .proto 檔案。定義一個 .proto 檔案很簡單:新增一個訊息到資料結構,然後指定一個和一個型別到每一個欄位,如下是本次例子使用的addressbook.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];
    }

    repeated PhoneNumber phone=4;
}

message AddressBook {
    repeated Person person=1;
}

有如你所見的,語法類似於C++或Java。讓我們分塊理解他們。

@waiting …

7.4   編譯你的ProtocolBuffer

現在已經擁有了 .proto 檔案,下一步就是編譯生成相關的訪問類。執行編譯器 protoc 編譯你的 .proto 檔案。

  1. 如果還沒安裝編譯器則下載並按照README的安裝。
  2. 執行編譯器,指定源目錄和目標目錄,定位你的 .proto 檔案到源目錄,然後執行:
    protoc -I=$SRC_DIR --python_out=$DST_DIR addressbook.proto

因為需要使用Python類,所以 –python_out 選項指定了特定的輸出語言。

這個步驟會生成 addressbook_pb2.py 到目標目錄。

7.5   ProtocolBuffer API

不像生成的C++和Java程式碼,Python生成的類並不會直接為你生成存取資料的程式碼。而是(有如你在 addressbook_pb2.py 中見到的)生成訊息描述、列舉、和欄位,還有一些神祕的空類,每個對應一個訊息型別:

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

    class PhoneNumber(message.Message):
        __metaclass__=reflection.GeneratedProtocolMessageType
        DESCRIPTION=_PERSON_PHONENUMBER

    DESCRIPTOR=_PERSON

class AddressBook(message.Message):
    __metaclass__=reflection.GeneratedProtocolMessageType
    DESCRIPTOR=_ADDRESSBOOK

這裡每個類最重要的一行是 __metaclass__=reflection.GeneratedProtocolMessageType 。通過Python的元類機制工作,你可以把他們看做是生成類的模板。在載入時, GeneratedProtocolMessageType 元類使用特定的描述符建立Python方法。隨後你就可以使用完整的功能了。

最後就是你可以使用 Person 類來操作相關欄位了。例如你可以寫:

import addressbook_pb2
person=addressbook_pb2.Person()
person.id=1234
person.name="John Doe"
person.email="[email protected]"
phone=person.phone.add()
phone.number="555-4321"
phone.type=addressbook_pb2.Person.HOME

需要注意的是這些賦值屬性並不是簡單的增加新欄位到Python物件,如果你嘗試給一個 .proto 檔案中沒有定義的欄位賦值,就會丟擲 AttributeError 異常,如果賦值型別錯誤會丟擲 TypeError 。在給一個欄位賦值之前讀取會返回預設值:

person.no_such_field=1  #raise AttributeError
person.id="1234"        #raise TypeError

更多相關資訊參考( http://code.google.com/apis/protocolbuffers/docs/reference/python-generated.html )。

7.5.1   列舉

列舉在元類中定義為一些符號常量對應的數字。例如常量 addressbook_pb2.Person.WORK 擁有值2。

7.5.2   標準訊息方法

每個訊息類包含一些其他方法允許你檢查和控制整個訊息,包括:

  • IsInitialized() :檢查是否所有必須(required)欄位都已經被賦值了。
  • __str__() :返回人類可讀的訊息表示,便於除錯。
  • CopyFrom(other_msg) :使用另外一個訊息的值來覆蓋本訊息。
  • Clear() :清除所有元素的值,回到初識狀態。

這些方法是通過介面 Message 實現的,更多訊息參考( http://code.google.com/apis/protocolbuffers/docs/reference/python/google.protobuf.message.Message-class.html )。

7.5.3   解析與序列化

最後,每個ProtocolBuffer類有些方法用於讀寫訊息的二進位制資料( http://code.google.com/apis/protocolbuffers/docs/encoding.html )。包括:

  • SerializeToString() :序列化,並返回字串。注意是二進位制格式而非文字。
  • ParseFromString(data) :解析資料。

他們是成對使用的,提供二進位制資料的序列化和解析。另外參考訊息API參考( http://code.google.com/apis/protocolbuffers/docs/reference/python/google.protobuf.message.Message-class.html )瞭解更多資訊。

Note

ProtocolBuffer與面向物件設計

ProtocolBuffer類只是用於存取資料的,類似於C++中的結構體,他們並沒有在面向物件方面做很好的設計。如果你想要給這些類新增更多的行為,最好的方法是包裝(wrap)。包裝同樣適合於複用別人寫好的 .proto 檔案。這種情況下,你可以把ProtocolBuffer生成類包裝的很適合於你的應用,並隱藏一些資料和方法,暴露有用的函式等等。你不可以通過繼承來給自動生成的類新增行為。 這會破壞他們的內部工作機制。

7.6   寫訊息

現在開始嘗試使用ProtocolBuffer的類。第一件事是讓地址本應用可以記錄聯絡人的細節資訊。想要做這些需要先建立聯絡人例項,然後寫入到輸出流。

這裡的程式從檔案讀取地址本,新增新的聯絡人資訊,然後寫回新的地址本到檔案。

#! /usr/bin/python
import addressbook_pb2
import sys

#這個函式使用使用者輸入填充聯絡人資訊
def PromptForAddress(person):
    person.id=int(raw_input("Enter person ID number: "))
    person.name=raw_input("Enter name: ")
    email=raw_input("Enter email address (blank for none): ")
    if email!="":
        person.email=email
    while True:
        number=raw_input("Enter a phone number (or leave blank to finish): ")
        if number=="":
            break
        phone_number=person.phone.add()
        phone_number.number=number
        type=raw_input("Is this a mobile, home, or work phone? ")
        if type=="mobile":
            phone_number.type=addressbook_pb2.Person.MOBILE
        elif type=="home":
            phone_number.type=addressbook_pb2.Person.HOME
        elif type=="work":
            phone_number.type=addressbook_pb2.Person.WORK
        else:
            print "Unknown phone type; leaving as default value."

#主函式,從檔案讀取地址本,新增新的聯絡人,然後寫回到檔案
if len(sys.argv)!=2:
    print "Usage:",sys.argv[0],"ADDRESS_BOOK_FILE"
    sys.exit(-1)

address_book=addressbook_pb2.AddressBook()

#讀取已經存在的地址本
try:
    f=open(sys.argv[1],"fb")
    address_book.ParseFromString(f.read())
    f.close()
except OSError:
    print sys.argv[1]+": Count open file. Creating a new one."

#新增地址
PromptFromAddress(address_book.person.add())

#寫入到檔案
f=open(sys.argv[1],"wb")
f.write(address_book.SerializeToString())
f.close()

7.7   讀訊息

當然,一個無法讀取的地址本是沒什麼用處的,這個例子讀取剛才建立的檔案並列印所有資訊:

#! /usr/bin/python

import addressbook_pb2
import sys

#遍歷地址本中所有的人並打印出來
def ListPeople(address_book):
    for person in address_book.person:
        print "Person ID:",person.id
        print "  Name:",person.name
        if person.HasField("email"):
            print "  E-mail:",person.email
        for phone_number in person.phone:
            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

#主函式,從檔案讀取地址本
if len(sys.argv)!=2:
    print "Usage:",sys.argv[0],"ADDRESS_BOOK_FILE"
    sys.exit(-1)

address_book=addressbook_pb2.AddressBook()

#讀取整個地址本檔案
f=open(sys.argv[1],"rb")
address_book.ParseFromString(f.read())
f.close()

ListPeople(address_book)

7.8   擴充套件ProtocolBuffer

在你發不了程式碼以後,可能會想要改進ProtocolBuffer的定義。如果你想新的資料結構向後相容,而你的舊資料可以向前相容,那麼你就找對了東西了,不過有些規則需要遵守。在新版本的ProtocolBuffer中:

  • 必須不可以改變已經存在的標籤的數字。
  • 必須不可以增加或刪除必須(required)欄位。
  • 可以刪除可選(optional)或重複(repeated)欄位。
  • 可以新增新的可選或重複欄位,但是必須使用新的標籤數字,必須是之前的欄位所沒有用過的。

這些規則也有例外( http://code.google.com/apis/protocolbuffers/docs/proto.html#updating ),不過很少使用。

如果你遵從這些規則,舊程式碼會很容易的讀取新的訊息,並簡單的忽略新的欄位。而對舊的被刪除的可選欄位也會簡單的使用他們的預設值,被刪除的重複欄位會自動為空。新的程式碼也會透明的讀取舊的訊息。然而,需要注意的是新的可選訊息不會在舊的訊息中顯示,所以你需要使用 has_ 嚴格的檢查他們是否存在,或者在 .proto 檔案中提供一個預設值。如果沒有預設值,就會有一個型別相關的預設預設值:對於字串就是空字串;對於布林型則是false;對於數字型別預設為0。同時要注意的是如果你添加了新的重複欄位,你的新程式碼不會告訴你這個欄位為空(新程式碼)也不會,也不會(舊程式碼)包含 has_ 標誌。

7.9   高階使用

ProtocolBuffer不僅僅提供了資料結構的存取和序列化。檢視Python API參考( http://code.google.com/apis/protocolbuffers/docs/reference/python/index.html )瞭解更多功能。

一個核心功能是通過訊息類的對映(reflection)提供的。你可以通過它遍歷訊息的所有欄位,和管理他們的值。關於對映的一個很有用的地方是轉換到其他編碼,如XML或JSON。一個使用對映的更高階的功能是尋找同類型兩個訊息的差異,或者開發出排序、正則表示式等功能。使用你的創造力,還可以用ProtocolBuffer實現比你以前想象的更多的問題。

對映是通過訊息介面提供的。

8   參考概覽

@waiting …

9   C++程式碼生成

@waiting …

10   C++ API

@waiting …

11   Java程式碼生成

@waiting …

12   Java API

@waiting …

13   Python程式碼生成

本頁提供了Python生成類的相關細節。你可以在閱讀本文件之前檢視語言指導。

Python的ProtocolBuffer實現與C++和Java的略有不同,編譯器只輸出構建程式碼的描述符來生成類,而由Python的元類來執行工作。本文件描述了元類開始生效以後的東西。

13.1   編譯器的使用

ProtocolBuffer通過編譯器的 –python_out= 選項來生成Python的相關類。這個引數實際上是指定輸出的Python類放在哪個目錄下。編譯器會為每個 .proto 檔案生成一個對應的 .py 檔案。輸出檔名與輸入檔名相關,不過有兩處修改:

  • 副檔名 .proto 改為 .py 。
  • 路徑名的修改。

如果你按照如下呼叫編譯器:

protoc --proto_path=src --python_out=build/gen src/foo.proto src/bar/baz.proto

編譯器會自動讀取兩個 .proto 檔案然後產生兩個輸出檔案。在需要時編譯器會自動建立目錄,不過 –python_out 指定的目錄不會自動建立。

需要注意的是,如果 .proto 檔名或路徑包含有無法在Python中使用的模組名(如連字元),就會被自動轉換為下劃線。所以檔案 foo-bar.proto 會變成foo_bar_pb2.py

Note

在每個檔案字尾的 _pb2.py 中的2代表ProtocolBuffer版本2。版本1僅在Google內部使用,但是你仍然可以在以前釋出的一些程式碼中找到它。自動版本2開始,ProtocolBuffer開始使用完全不同的介面了,從此Python也沒有編譯時型別檢查了,我們加上這個版本號來標誌Python檔名。

13.2   包

Python程式碼生成根本不在乎包的名字。因為Python使用目錄名來做包名。

13.3   訊息

先看看一個簡單的訊息宣告:

message Foo {}

ProtocolBuffer編譯器會生成類Foo,它是 google.protobuf.Message 的子類。這個實體類,不含有虛擬方法。不像C++和Java,Python生成類對優化選項不感冒;實際上Python的生成程式碼已經為程式碼大小做了優化。

你不能繼承Foo的子類。生成類被設計不可以被繼承,否則會被打破一些設計。另外,繼承本類也是不好的設計。

Python的訊息類沒有特定的公共成員,而是定義介面,極其巢狀的欄位、訊息和列舉型別。

一個訊息可以在另外一個訊息中宣告,例如 message Foo { message Bar {}} 。在這種情況下,Bar類定義為Foo的一個靜態成員,所以你可以通過 Foo.Bar 來引用。

13.4   欄位

對於訊息型別中的每一個欄位,都有對應的同名成員。

13.4.1   簡單欄位

如果你有一個簡單欄位(包括可選的和重複的),也就是非訊息欄位,你可以通過簡單欄位的方式來管理,例如foo欄位的型別是int32,你可以:

message.foo=123
print message.foo

注意設定foo的值,如果型別錯誤會丟擲TypeError。

如果foo在賦值之前就讀取,就會使用預設值。想要檢查是否已經賦值,可以用 HasField() ,而清除該欄位的值用 ClearField() 。例如:

assert not message.HasField("foo")
message.foo=123
assert message.HasField("foo")
message.ClearField("foo")
assert not message.HasField("foo")

13.4.2   簡單訊息欄位

訊息型別工作方式略有不同。你無法為一個嵌入訊息欄位賦值。而是直接操作這個訊息的成員。因為例項化上層訊息時,其包含的子訊息同時也例項化了,例如定義:

message Foo {
    optional Bar bar=1;
}

message bar {
    optional int32 i=1;
}

你不可以這麼做,因為不能做訊息型別欄位的賦值:

foo=Foo()
foo.bar=Bar()   #WRONG!

而是可以直接對訊息型別欄位的成員賦值:

foo=Foo()
assert not foo.HasField("bar")
foo.bar.i=1
assert foo.HasField("bar")

注意簡單的讀取訊息型別欄位的未賦值成員只不過是列印其預設值:

foo=Foo()
assert not foo.HasField("bar")
print foo.bar.i #列印i的預設值
assert not foo.HasField("bar")

13.4.3   重複欄位

重複欄位表現的像是Python的序列型別。如果是嵌入的訊息,你無法為欄位直接賦值,但是你可以管理。例如給定的定義:

message Foo {
    repeated int32 nums=1;
}

你就可以這麼做:

foo=Foo()
foo.nums.append(15)
foo.nums.append(32)
assert len(foo.nums)==2
assert foo.nums[0]==15
assert foo.nums[1]==32
for i in foo.nums:
    print i
foo.nums[1]=56
assert foo.nums[1]==56

作為一種簡單欄位,清除該欄位必須使用 ClearField() 。

13.4.4   重複訊息欄位

重複訊息欄位工作方式與重複欄位很像,除了 add() 方法用於返回新的物件以外。例如如下定義:

message Foo {
    repeated Bar bar=1;
}

message Bar {
    optional int32 i=1;
}

你可以這麼做:

foo=Foo()
bar=foo.bars.add()
bar.i=15
bar=foo.bars.add()
bar.i=32
assert len(foo.bars)==2
assert foo.bars[0].i==15
assert foo.bars[1].i==32
for bar in foo.bars:
    print bar.i
foo.bars[1].i=56
assert foo.bars[1].i==56

13.4.5   列舉型別

@waiting …

13.4.6   擴充套件

@waiting …

13.5   服務

13.5.1   介面

一個簡單的介面定義:

service Foo {
    rpc Bar(FooRequest) returns(FooResponse);
}

ProtocolBuffer的編譯器會生成類 Foo 來展示這個服務。 Foo 將會擁有每個服務定義的方法。在這種情況下 Bar 方法的定義是:

def Bar(self,rpc_controller,request,done)

引數等效於 Service.CallMethod() ,除了隱含的 method_descriptor 引數。

這些生成的方法被定義為可以被子類過載。預設實現只是簡單的呼叫 controller.SetFailed() 而丟擲錯誤資訊告之尚未實現。然後呼叫done回撥。在實現你自己的服務時,你必須繼承生成類,然後過載各個介面方法。

Foo繼承了 Service 介面。ProtocolBuffer編譯器會自動聲響相關的實現方法:

  • GetDescriptor :返回服務的 ServiceDescriptor 。
  • CallMethod :檢測需要呼叫哪個方法,並且直接呼叫。
  • GetRequestClass 和 GetResponseClass :返回指定方法的請求和響應類。

13.5.2   存根(Stub)

ProtocolBuffer編譯器也會為每個服務介面提供一個存根實現,用於客戶端傳送請求到伺服器。對於Foo服務,存根實現是 Foo_Stub 。

Foo_Stub 是Foo的子類,他的構造器是一個 RpcChannel 。存根會實現呼叫每個服務方法的 CallMethod() 。

ProtocolBuffer哭並不包含RPC實現。然而,它包含了你構造服務類的所有工具,不過選擇RPC實現則隨你喜歡。你只需要提供 RpcChannel 和 RpcController 的實現即可。

14   Python API

@waiting …

15   其他語言

http://www.cppblog.com/liquidx

相關推薦

ProtoBuf開發者指南大全Google Protocol Buffer協議

目錄 1   概覽 1.1   什麼是protocol buffer 1.2   他們如何工作 1.3   為什麼不用XML? 1.4   聽起來像是為我的解決方案,如何開始? 1.5   一點歷史 2   語言指導 2.1   定義一個訊息型別 2.2   值型別 2.3   可選欄位與預設值 2.4  

Android 使用Google Protocol buffer協議

什麼是 Protocol buffer 它是用於對結構化資料進行序列化的一種靈活、高效、自動化的機制——類似XML,但是更小、更快、更簡單。您可以定義資料的結構化方式,然後使用特殊生成的原始碼輕鬆地在各種資料流和使用的各種語言之間讀寫結構化資料。 對於使用 J

【小松教你手遊開發】【unity實用技能】Google Protocol Bufferprotobuf 使用和研究

由於專案使用的是c#,所以下面的範例也是用於c# 一、安裝Google Protocol Buffer 二、編寫一個bat檔案處理檔案,批量生成c#檔案,如: @echo off SETLOCAL ENABLEDELAYEDEXPANSION rem 查詢檔案

Google Protocol Buffer序列化入門實戰附原始碼

Google Protocol Buffer入門實戰(附原始碼) Google Protocol Buffer(後面簡稱PB)是Google開源的一款二進位制序列化工具,佔用空間小,傳輸效率高。最近由於專案中使用到了PB,所以特地學習了PB,這篇文章也是自己學

關於google protocol bufferPB的優缺點和一些個人的理解

--------------------------------   網路名稱:name   網路型別:type   卷積層引數:convolution_param --------------------------------   如果使用protobuf實現,首先要寫一個proto檔案(不妨叫VGG.

google protocol buffer——protobuf的基本使用和模型分析

這一系列文章主要是對protocol buffer這種編碼格式的使用方式、特點、使用技巧進行說明,並在原生protobuf的基礎上進行擴充套件和優化,使得它能更好地為我們服務。   1.什麼是protobuf protocol buffer是由google推出一種資料編碼格式,不依賴平臺和語言,類似

google protocol buffer——protobuf的使用特性及編碼原理

這一系列文章主要是對protocol buffer這種編碼格式的使用方式、特點、使用技巧進行說明,並在原生protobuf的基礎上進行擴充套件和優化,使得它能更好地為我們服務。   在上一篇文章中,我們展示了protobuf在java中的基本使用方式。而本文將繼續深入探究protobuf的編碼原理。

google protocol buffer——protobuf的編碼原理二

這一系列文章主要是對protocol buffer這種編碼格式的使用方式、特點、使用技巧進行說明,並在原生protobuf的基礎上進行擴充套件和優化,使得它能更好地為我們服務。   在上一篇文章中,我們主要通過一些示例瞭解了protobuf的使用特性,以及和這些特性相關的基礎編碼原理。 編碼原理只開

google protocol buffer——protobuf的問題及改進一

這一系列文章主要是對protocol buffer這種編碼格式的使用方式、特點、使用技巧進行說明,並在原生protobuf的基礎上進行擴充套件和優化,使得它能更好地為我們服務。 在上一篇文章中,我們完整了解了protobuf的編碼原理,那麼在這篇文章中,我將會展示在使用過程中遇到的問題,以及解決方案。並在此

google protocol buffer——protobuf的問題和改進2

這一系列文章主要是對protocol buffer這種編碼格式的使用方式、特點、使用技巧進行說明,並在原生protobuf的基礎上進行擴充套件和優化,使得它能更好地為我們服務。 在上一篇文章中,我們舉例了在移動網際網路場景下原生protobuf類庫使用上的問題,並且自己完成了一個java的編碼類庫。本文中將

Google Protocol Buffer入門

解釋 -- rate plugin gcc 入門 新增 查找 abs 簡介 Google Protocol Buffer( 簡稱 Protobuf) 是 Google 公司內部的混合語言數據標準,目前已經正在使用的有超過 48,162 種報文格式定義和超過 12,183 個

Google Protocol Buffer

長度 .com ID 成員 得到 規模 c 語言 ostream HR 簡介 什麽是 Google Protocol Buffer? 假如您在網上搜索,應該會得到類似這樣的文字介紹: Google Protocol Buffer( 簡稱 Protobuf) 是 Google

windows下Google Protocol Buffer 編譯安裝使用教程

轉載修改自:http://kuaile.in/archives/1214 protobuf的全稱是Protocol Buffer,它是google 的一種資料交換的格式,可用於用於分散式應用之間的資料通訊或者異構環境下的資料交換, 最近因為專案的需求,需要接觸Protobuf,在官方提供的壓縮包

Google Protocol Buffer 的使用和原理

簡介 Google Protocol Buffer( 簡稱 Protobuf) 是 Google 公司內部的混合語言資料標準。Protobuf是一種輕便高效的結構化資料儲存格式,可以用於結構化資料序列化,或者說序列化。它很適合做資料儲存或 RPC 資料交換格式。可用於通

C++ Class Mapped Google Protocol Buffer Message

摘要 Google Protocol Buffer 是一個優秀的基於二進位制的網路訊息編解碼框架。應用於專案時,可以節省不少的人力資源、開發時間和程式BUG。但其不足之處是protobuf編譯器生成的C++訊息類(或者Java等其他語言的訊息類)冗餘資料過多,需要依賴於pro

從環境搭建開始學習使用Google Protocol Buffer和gRPC

首先安裝 golang/protobuf Golang 語言版本的 API 時,需要先安裝標準 C++ 實現的 protocol buffer google/protobuf,使用linux的話就是先到github上下載對應的二進位制檔案 下載完解壓之後將bin目錄加

5高通AP10.4開發者指南——WLAN1.5 WLAN驅動模組化的一些其他修改

1.5 WLAN驅動模組化的一些其他修改 從QCA_Networking_2016.SPF.4.0版本開始,OL和DA驅動相互獨立,並新建了一個UMAC模組,作為通用層,並獨立於OL和DA的模組。 因為DA驅動已經獨立於“UMAC+OL”驅動結構,所以將UM

8高通AP10.4開發者指南——WLAN2.2 上下文及同步處理

2.2 上下文及同步處理 WLAN驅動在不同的上下文處理中執行,比如 ISR上下文 Softirq(軟中斷)/tasklet(核心軟中斷延遲機制)上下文 Process(程序)上下文 2.2.1 ISR處理 WLAN裝置成功附著之後(ath_atta

2高通AP10.4開發者指南——WLAN1.2 WLAN軟體架構

1.2 WLAN軟體架構 WLAN驅動層被封裝成數個部分,每個部分都提供了API,方便使用者定製自己的AP軟體和進行二次開發。 圖1-3 WLAN軟體整體架構說明 1.2.1 硬體抽象層(HAL) 硬體抽象層(HAL)是驅動和硬體晶片之間的連線部

Google Protocol Buffer 的使用(一)

一、什麼是Google Protocol Buffer下面是官網給的解釋:Protocol buffers are a language-neutral, platform-neutral extensible mechanism for serializing structured data. – thin