pythonic和高性能协议缓冲区实现。
cprotobuf的Python项目详细描述
一个使用cython的最小快速protobuf实现。 基准测试表明它比google官方的expremental cpp-python实现快得多。
我从2013年开始在生产中使用它,只在python2.7中测试过,欢迎对其他python版本的反馈。
基准
$ ./setup.py build_ext --inplace $ cd benchmark $ ./bench.sh encode[google official pure python]: 10 loops, best of 3: 68.8 msec per loop encode[google official cpp python]: 100 loops, best of 3: 19.4 msec per loop encode[py-protobuf][cprotobuf]: 100 loops, best of 3: 3.58 msec per loop decode[google official pure python]: 10 loops, best of 3: 47.5 msec per loop decode[google official cpp python]: 100 loops, best of 3: 4.55 msec per loop decode[py-protobuf][cprotobuf]: 100 loops, best of 3: 3.98 msec per loop
教程
使用插件
你这样写一个person.proto文件:
packagefoo;messagePerson{requiredint32id=1;requiredstringname=2;optionalstringemail=3;}
还有一个people.proto这样的文件:
packagefoo;import"person.proto";messagePeople{repeatedPersonpeople=1;}
然后使用提供的插件编译它:
$ protoc --cprotobuf_out=. person.proto people.proto
如果在windows上运行protobuf插件有困难,可以直接运行protoc-gen-cprotobuf,如下所示:
$ protoc -ofoo.pb person.proto people.proto $ protoc-gen-cprotobuf foo.pb -d .
然后得到一个python模块foo_pb.py,cprotobuf为每个包而不是每个协议文件生成一个python模块。
生成的代码非常可读:
# coding: utf-8fromcprotobufimportProtoEntity,Field# file: person.protoclassPerson(ProtoEntity):id=Field('int32',1)name=Field('string',2)email=Field('string',3,required=False)# file: people.protoclassPeople(ProtoEntity):people=Field(Person,1,repeated=True)
实际上,如果只使用python,可以编写这个python模块,避免代码生成。
api
现在,您有了这个可爱的python模块,如何解析和序列化消息?
在设计这个包时,我们尽量减少迁移的工作量,因此我们将api的名称与协议缓冲区的名称保持相似。
注意
因为这不需要重用消息实例并在python中对其调用Clear,所以它不提供Clearapi, 所以ParseFromString更像是官方实现中的MergeFromString,因为它一开始不会调用Clear。
编码/解码
>>>fromfoo_pbimportPerson,People>>>msg=People()>>>msg.people.add(...id=1,...name='jim',...email='jim@gmail.com',...)>>>s=msg.SerializeToString()>>>msg2=People()>>>msg2.ParseFromString(s)>>>len(msg2)1>>>msg2.people[0].name'jim'
反射
>>>fromfoo_pbimportPerson,People>>>dir(Person._fields[0])['__class__','__delattr__','__doc__','__format__','__get__','__getattribute__','__hash__','__init__','__new__','__pyx_vtable__','__reduce__','__reduce_ex__','__repr__','__setattr__','__sizeof__','__str__','__subclasshook__','index','name','packed','repeated','required','wire_type']>>>Person._fields[0].name'email'>>>Person._fieldsmap{1:<cprotobuf.Fieldobjectat0xb74a538c>,2:<cprotobuf.Fieldobjectat0xb74a541c>,3:<cprotobuf.Fieldobjectat0xb74a5c8c>}>>>Person._fieldsmap_by_name{'email':<cprotobuf.Fieldobjectat0xb74a5c8c>,'name':<cprotobuf.Fieldobjectat0xb74a541c>,'id':<cprotobuf.Fieldobjectat0xb74a538c>}
重复容器
我们使用RepeatedContainer来表示重复字段,RepeatedContainer是从list继承的,因此您可以像list那样对其进行操作,或者像google的实现那样使用api。
>>>fromfoo_pbimportPerson,People>>>msg=People()>>>msg.people.add(...id=1,...name='jim',...email='jim@gmail.com',...)>>>p=msg.people.add()>>>p.id=2>>>p.name='jake'>>>p.email='jake@gmail.com'>>>p2=Person(id=3,name='lucy',email='lucy@gmail.com')>>>msg.people.append(p2)>>>msg.people.append({...'id':4,...'name':'lily',...'email':'lily@gmail.com',...})
快速编码原始数据
如果您已经将消息表示为list和dict,则可以对其进行编码,而无需构造中间对象,从而获得大量开销:
>>>fromcprotobufimportencode_data>>>fromfoo_pbimportPerson,People>>>s=encode_data(People,[...{'id':1,'name':'tom','email':'tom@gmail.com'}...])>>>msg=People()>>>msg.ParseFromString(s)>>>msg.people[0].name'tom'
运行测试
$ nosetests