在Python中循环遍历Protocol Buffers属性

25
我希望能够递归遍历协议缓冲区消息中包含的所有属性/子对象,假设我们不知道它们的名称或数量。以下是来自Google网站教程的.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;
}

并且要使用它...

person = tutorial.Person()
person.id = 1234
person.name = "John Doe"
person.email = "jdoe@example.com"
phone = person.phone.add()
phone.number = "555-4321"
phone.type = tutorial.Person.HOME

给定一个名为Person的对象,如何访问每个属性的名称和值: person.id, person.name, person.email, person.phone.number, person.phone.type?

我尝试过以下方法,但是它似乎不会递归到person.phone.numberperson.phone.type

object_of_interest = Person

while( hasattr(object_of_interest, "_fields") ):
  for obj in object_of_interest._fields:
    # Do_something_with_object(obj) # eg print obj.name
    object_of_interest = obj

我尝试使用obj.DESCRIPTOR.fields_by_name.keys来访问子元素,但这些是子对象的字符串表示,而不是对象本身。

obj.name给出了名称属性,但我不确定如何实际获取该属性的值,例如obj.name可能会给出“name”,但我如何从中获取“john doe”?

2个回答

43

我对protobuf不是很熟悉,因此可能会有更简单的方法或api来处理这种情况。但以下示例显示了如何迭代/审查对象字段并将其打印出来。希望至少能为您提供正确方向上的足够信息...

import addressbook_pb2 as addressbook

person = addressbook.Person(id=1234, name="John Doe", email="foo@example.com")
person.phone.add(number="1234567890")


def dump_object(obj):
    for descriptor in obj.DESCRIPTOR.fields:
        value = getattr(obj, descriptor.name)
        if descriptor.type == descriptor.TYPE_MESSAGE:
            if descriptor.label == descriptor.LABEL_REPEATED:
                map(dump_object, value)
            else:
                dump_object(value)
        elif descriptor.type == descriptor.TYPE_ENUM:
            enum_name = descriptor.enum_type.values[value].name
            print "%s: %s" % (descriptor.full_name, enum_name)
        else:
            print "%s: %s" % (descriptor.full_name, value)

dump_object(person)

输出

tutorial.Person.name: John Doe
tutorial.Person.id: 1234
tutorial.Person.email: foo@example.com
tutorial.Person.PhoneNumber.number: 1234567890
tutorial.Person.PhoneNumber.type: HOME

这是一个很好的回答,但协议缓冲区要求这样的无聊操作来迭代它们,真是太疯狂了。如果一旦接收到数据后处理起来如此费力,那么拥有这种高效、紧凑的数据传输形式还有什么意义呢? - Mike Williamson

0
如果你想获取底层proto类中定义的所有字段(而不仅仅是给定实例中设置的字段),你可以这样做:
def print_fields(message_desc):
  for field in message_desc.fields:
    if field.type == field.TYPE_MESSAGE:
      print_fields(field.message_type)
    else:
      print(field.full_name)

然后在消息的描述符上调用它,例如:print_fields(tutorial.Person.DESCRIPTOR)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接