69.Protobuf进阶——使用Pyt
- 定义addressbookproto
- 编译Protocol buffer
- 使用python的Protobuf api
- 枚举
- 标准message方法
- 序列化和解析
- 将message写入文件
- 从文件读取message对象
- message的赋值
转载请注明原始链接:Http://blog.csdn.net/a464057216/article/details/54932719
用一个例子说明使用Python操作PB的方法:
1.定义.proto
文件。
2.编译.proto
文件产出Python代码。
3.使用Python API读写message。
该例子完成一个地址簿程序,能够对地址簿信息进行读写,地址簿中每个人的信息包括姓名、ID、email、联系电话。
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 phones = 4;
}
message AddressBook {
repeated Person people = 1;
}
protoc --python_out=. addressbook.proto
生成addressbook_pb2.py
在Python脚本中使用addressbook_pb2.py:
import addressbook_pb2 as addressbook
person = addressbook.Person()
person.id = 1234
person.name = "John Doe"
person.email = "jdoe@example.com"
phone = person.phones.add() #phones字段是符合类型,调用add()方法初始化新实例。如果phones字段是标量类型,直接append()添加新元素即可。
phone.number = "555-4321"
phone.type = addressbook.Person.HOME
如果访问.proto文件中未定义的域,抛出AttributeError,如果为某个域赋予了错误类型的值,抛出TypeError。在某个域未赋值前访问该域,返回这个域的默认值。
枚举
有整型值的符号常量,比如addressbook.Person.WORK的值是2。
标准message方法
每个Message类含有一些检查或操作整个message的方法,比如:
• IsInitialized():检查是否所有required域都已赋值。
• str():返回message的可读形式,可以通过str(message)或者print message触发,用于调试代码。
• Clear():将所有域的赋值清空。
• MergeFrom(other_msg):将给定的other_msg的内容合并到当前message,独立的域使用other_msg的值覆盖写入,repeated域的内容append到当前message的对应字段。独立的子message和group被递归的合并。
• CopyFrom(other_msg):先对本message调用Clear()方法,再调用MergeFrom(other_msg)。
• MergeFromString(serialized):将PB二进制字符串解析后合并到本message,合并规则与MergeFrom方法一致。
• ListFields():以(Google.protobuf.descriptor.FieldDescriptor,value)的列表形式返回非空的域,独立的域如果HasField返回True则是非空的,repeated域至少包含一个元素则是非空的。
• ClearField(field_name):清空某个域,如果被清空的域名不存在,抛出ValueError异常。
• ByteSize():返回message占用的空间大小。
• WichOneof(oneof_group):返回oneof组中被设置的域的名字或None,如果提供的oneof的组名不存在,抛出ValueError异常。比如test.proto
中内容如下:
message Test {
required string a = 1;
optional float b = 2;
oneof l {
string c = 3;
int32 d = 4;
float e = 5;
}
}
调用WhichOneof的代码如下:
import test_pb2 as test
t1 = test.Test()
t1.a = "t1"
t1.b = 1.0
t1.c = "oneof c"
print t1.WhichOneof('l')
运行输出:
c
序列化和解析
每个Message类都有序列化和解析方法:
• SerializeToString():将message序列化并返回str类型的结果(str类型只是二进制数据的一个容器而已,而不是文本内容)。如果message没有初始化,抛出message.EncodeError异常。
• SerializePartialToString():将message序列化并返回str类型的结果,但是不检查message是否初始化。
• ParseFromString(data):从给定的二进制str解析得到message对象。
如果要在生成的PB类的基础上增加新的功能,应该采用包装(wrapper)的方式,永远不要将PB类作为基类派生子类添加新功能。
将message写入文件
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.phones.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 "Unkown 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()
# Read the existing address book.
try:
f = open(sys.argv[1], "rb")
address_book.ParseFromString(f.read())
f.close()
except IOError:
print sys.argv[1] + ": Could not open file. Creating a new one."
# Add an address.
PromptForAddress(address_book.people.add())
# Write the new address book back to disk.
f = open(sys.argv[1], "wb")
f.write(address_book.SerializeToString())
f.close()
从文件读取message对象
import addressbook_pb2
import sys
def ListPeople(address_book):
for person in address_book.people:
print "Person ID:", person.id
print " Name:", person.name
if person.HasField("email"):
print " E-mail adress:", 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
if len(sys.argv) != 2:
print "Usage:", sys.argv[0], "ADDRESS_BOOK_FILE"
sys.exit(-1)
address_book = addressbook_pb2.AddressBook()
# Read the existing address book
f = open(sys.argv[1], "rb")
address_book.ParseFromString(f.read())
f.close()
ListPeople(address_book)
如果Message.HasField(field_name)的参数对应的域规则是optional,且该域没有设置值,返回False,如果对应的域规则是repeated,且该域没有设置值,抛出ValueError异常。
message中,标量类型和枚举类型的域,必须通过message.field_name=value的格式赋值,message类型的域,可以使用tmp=message.field_name赋值给tmp后,通过操作tmp赋值。当然,message类型的域也可以使用同标量赋值一样的格式赋值。
比如test.proto
内容为:
message Test {
required inner a = 1;
message inner {
required string a = 2;
optional int32 b =3;
}
optional Color b = 4;
enum Color {
RED = 0;
GREEN = 1;
}
optional string c = 5;
}
赋值的代码为:
import test_pb2 as test
t1 = test.Test()
t1.c = "Scalar"
a = t1.a
a.a = "message string"
a.b = 1
# 使用如下方式赋值也可以
# t1.a.a = "message string"
# t1.a.b = 1
t1.b = test.Test.RED
print "t1:\n", t1
如果觉得我的文章对您有帮助,欢迎关注我(CSDN:Mars Loo的博客)或者为这篇文章点赞,谢谢!
相关文章