# PythonでProtocol Buffersの文字列と数値の単純なメッセージを操作する
gRPC (opens new window)のシリアライズのフォーマットとして使われるProtocol Buffers (opens new window)をさわってみる。
protobufをインストールし、.proto
ファイルからPythonのコードを生成する。そのコードを使ってシリアライズ、デシリアライズを試す。
# Protocol Buffersとは
Protocol Buffersは、構造化されたデータをシリアライズする仕組みだ。
構造化されたデータとは、値とその値がどんな意味をもつのか整理されたデータだ。
シリアライズとは、プログラムで使われているメモリ内のオブジェクトを、ディスクに保存したりネットワーク経由で送信したりするためにバイトストリーム(一連のバイト列)に変換することだ。
# Protocol Buffersを用いた開発
Protocol Buffersを用いた開発は次の手順で行なう。
.proto
ファイルを作成する.proto
ファイルを指定した言語のコードへ変換(コンパイル)する- 変換されたコードを使い、オブジェクトをシリアライズおよびデシリアライズ(バイトストリームからオブジェクトへの変換)する
実際にファイルを作成しながら、1つずつ見ていく。
# .protoファイルを作成する
.proto
ファイルはコードを生成する元になるファイルだ。
ファイルは独自の構文 (opens new window)にしたがって記述していく。
最初の行のsyntax
にはファイル内で使う構文のバージョンを記載する。指定しない場合proto2
が使われる。2019/6/1時点ではproto3
が最新であるため、新しい構文を使いたい場合は明示的に指定する必要がある。
コード中でオブジェクトと呼ばれているものは、.proto
ファイルではメッセージと呼ばれている。
メッセージを定義するにはmessage メッセージ名 {}
のようにmessage
という文言に続いてメッセージ名を定義する。 メッセージはフィールドごとに型、フィールド名、一意な番号をつける。 型は文字列を示すstring
や整数を示すint32
だけでなく、浮動小数点を示すfloat
や真偽値を示すbool
が用意されている (opens new window)。
person.proto
syntax = "proto3";
message Person {
string name = 1;
int32 id = 2;
string email = 3;
}
# .protoファイルを指定した言語のコードへ変換(コンパイル)する
作成した.proto
ファイルをPythonのコードに変換するため、protobuf
をインストールする。
Macではbrewを使いprotobuf
をインストールする。
$ brew install protobuf
$ protoc --version
libprotoc 3.7.1
インストールしたprotobuf
を使い、.protoファイル
からPythonのコードを出力する。
protoc --python_out=[Pythonのコードを出力するディレクトリ] [.protoファイルのパス]
$ protoc --python_out=./ ./person.proto
Pythonのコードは、.protoファイル
のファイル名に_pb2
の接尾語がついたファイル名で出力される。
.
├── person.proto
└── person_pb2.py
# 変換されたコードを使い、オブジェクトをシリアライズおよびデシリアライズする
出力されたperson_pb2.py
をインポートして、オブジェクトのシリアライズおよびデシリアライズを試していく。
# オブジェクトをシリアライズする
person_pb2
モジュールからPerson
クラスをインポートし、オブジェクトからバイナリー文字列を出力するSerializeToString()
メソッドや、バイナリー文字列からオブジェクトを生成するParseFromString(data)
メソッドを持つオブジェクトを生成する。
main.py
from person_pb2 import Person
person = Person()
person.id = 1234
person.name = "Jhon"
person.email = "jhon@example.com"
print(person.SerializeToString())
main.py
をそのまま実行すると以下のエラーが表示される。
person_pb2
モジュールで読み込んでいるPerson
クラスでgoogle.protobuf
を読み込んでいるが、依存関係を解消していないためエラーになる。
from google.protobuf import descriptor as _descriptor
ModuleNotFoundError: No module named 'google'
依存関係を解消するためにprotobuf
をpipenv
(pip
)でインストールする。
$ pipenv install protobuf
protobuf
をインストールした上で実行すると、バイナリー文字列が出力される。
$ pipenv run python main.py
b'\n\x04Jhon\x10\xd2\t\x1a\x10jhon@example.com'
# バイナリーをデシリアライズする
ParseFromString
でシリアライズ結果のバイナリー文字列を読み込む。
from person_pb2 import Person
person = Person()
print('before', person)
person.ParseFromString(b'\n\x04Jhon\x10\xd2\t\x1a\x10jhon@example.com')
print('after', person)
実行するとバイト文字列からオブジェクトが生成されていることがわかる。
$ python read.py
before
after name: "Jhon"
id: 1234
email: "jhon@example.com"
・参考
https://developers.google.com/protocol-buffers/ (opens new window)
https://developers.google.com/protocol-buffers/docs/pythontutorial (opens new window)
https://stackoverflow.com/questions/633402/what-is-serialization (opens new window)