如何列出在Google协议缓冲区中定义的属性?

4 投票
3 回答
7477 浏览
提问于 2025-05-01 14:53

我有一个谷歌协议缓冲区的定义,大概长这样:

message Foo {
   required string Name = 1;
   optional string Address = 2;
   optional string NickName = 3;
   optional  int32  height = 4;
}

现在在Python中,我想列出上面所有的属性,但只想要这些属性。然而,在交互式Python中,我发现还有很多其他字段是谷歌定义的。这就有点麻烦了。

我看了一些关于反射的StackOverflow帖子。有一个看起来不错的inspect模块,但问题是谷歌协议缓冲区为我定义的其他成员。

有没有办法做到这一点呢?

我想做的是,我有一个Python工具,可以通过命令行填写上面的字段。我使用argparse,然后我会:

parser = argparse.ArgumentParser(...)
parser.add_argument( "--attribute_name", blah)

我想把add_argument()放在一个循环里,让它根据proto文件的定义动态生成。基本上,我不想每次修改proto文件时都去改工具的代码。听起来我应该能在Python中做到这一点,但我就是不知道怎么做。

有没有人有建议呢?

谢谢。

补充一下,我用protoc编译了上面的例子。这里是交互式输出:

>>> import hello_pb2
>>> h = hello_pb2.Foo()
>>> dir(h)
['ADDRESS_FIELD_NUMBER', 'Address', 'ByteSize', 'Clear', 'ClearExtension', 'ClearField', 'CopyFrom', 'DESCRIPTOR', 'FindInitializationErrors', 'FromString', 'HEIGHT_FIELD_NUMBER', 'HasExtension', 'HasField', 'IsInitialized', 'ListFields', 'MergeFrom', 'MergeFromString', 'NAME_FIELD_NUMBER', 'NICKNAME_FIELD_NUMBER', 'Name', 'NickName', 'ParseFromString', 'RegisterExtension', 'SerializePartialToString', 'SerializeToString', 'SetInParent', '_InternalParse', '_InternalSerialize', '_Modified', '_SetListener', '__class__', '__deepcopy__', '__delattr__', '__doc__', '__eq__', '__format__', '__getattribute__', '__hash__', '__init__', '__metaclass__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', '__unicode__', '__weakref__', '_cached_byte_size', '_cached_byte_size_dirty', '_decoders_by_tag', '_extensions_by_name', '_extensions_by_number', '_fields', '_is_present_in_parent', '_listener', '_listener_for_children', 'height']

我检查了一些有希望的字段,比如_fields,但那是空的。

这里是答案:(由Kenton Varda的回复提供)

import hello_pb2
h = hello_pb2.Foo()
f = hello_pb2.Foo()
f.DESCRIPTOR.fields_by_name.keys()
暂无标签

3 个回答

0

在proto3中,我无法让@Kenton Varda的回答正常工作。

这个对我有效:

from hello_pb2 import Foo

Foo.DESCRIPTOR.values_by_name
1

如果你想把一个protobuf对象转换成字典,可以这样做:

import hello_pb2

def proto2dict(msg):
    hsh_val = dict(msg.ListFields())
    d = dict((k, hsh_val[hsh]) for k, hsh in msg.DESCRIPTOR.fields_by_name.items())
    return d

data = {"Name":"name", "Address":"address", "NickName":"nick", "height": 6}
msg = hello_pb2.Foo(**data)

assert proto2dict(msg) == data

注意:这样做可能效率不高,而且对于嵌套的定义可能不太适用。

11

你想要遍历 Foo.DESCRIPTOR.fields。可以看看 Descriptor 这个类:

https://developers.google.com/protocol-buffers/docs/reference/python/google.protobuf.descriptor.Descriptor-class

每个消息类都有一个静态成员 DESCRIPTOR,它是该类型的描述符。

撰写回答