查找 Python DESCRIPTOR for ProtoBuf 中列出的枚举
Find enums listed in Python DESCRIPTOR for ProtoBuf
我收到了一个使用 Python 的 Google ProtoBuf,我正在尝试将枚举的值与它的字符串表示形式进行比较。基于 this and this 我应该能够使用类似 enum_values_by_name
的东西来获取我需要的信息。但是,所有 enum*
相关属性都是空的:
>>> type(my_message)
<class 'myObjects_pb2.myObject'>
>>> my_message
# nID: 53564
# nAge: 2
# type: OBJECT_CLASS_SIGN
# view: OBJECT_VIEW_FRONT
>>> my_message.type
# 1
>>> filter(lambda s: s.startswith('enum'), dir(my_message.DESCRIPTOR))
# ['enum_types', 'enum_types_by_name', 'enum_values_by_name']
>>> my_message.DESCRIPTOR.enum_types
# []
>>> my_message.DESCRIPTOR.enum_types_by_name
# {}
>>> my_message.DESCRIPTOR.enum_values_by_name
# {}
可能是因为我的protobuf定义在很多文件中,而我想要的enums在我导入的主文件中没有定义(但用于解码my_message
)?
为什么我得到这些空集合以及(更重要的)如何找到关于枚举的信息?
我不知道为什么消息的 DESCRIPTOR
包含未填充的枚举属性。 (这对我来说似乎是一个错误。)但是,(至少)有两个解决方案:
1) 如果您知道定义枚举的文件的名称,则可以 'hack' 通过以下方式按名称获取枚举值:
# This is the root ProtoBuf definition
from mydir import rootapi_pb2
# We happen to know that the enums are defined in myenums.proto
enum_file = rootapi_pb2.myenums__pb2 # NOTE: Extra Underscore!
enum_value = getattr(enum_file, 'OBJECT_CLASS_SIGN')
但是,如果您不想依赖这个 hack,您最终可以通过以下方式找到枚举描述符,从而从名称中找到值:
my_message.DESCRIPTOR.fields_by_name['type'].enum_type.values_by_name['OBJECT_CLASS_SIGN'].number
因为这太可怕了,所以它被包装成一个安全的、可重复使用的函数:
def enum_value(msg, field_name, enum_name):
"""Return the integer for the enum name of a field,
or None if the field does not exist, is not an enum, or the
enum does not have a value with the supplied name."""
field = msg.DESCRIPTOR.fields_by_name.get(field_name,None)
if field and field.enum_type:
enum = field.enum_type.values_by_name.get(enum_name,None)
return enum and enum.number
print(enum_value(my_message, 'type', 'OBJECT_CLASS_SIGN'))
# 1
python 的 ProtoBuf 非常丑陋...但我还是不得不使用它...
我觉得@Phrogz 的功能可以稍微简化一下。这是我附带的功能:
def get_enum_name_by_number(parent, field_name):
field_value = getattr(parent, field_name)
return parent.DESCRIPTOR.fields_by_name[field_name].enum_type.values_by_number.get(field_value).name
print(my_message.type)
# 1
print(get_enum_name_by_number(my_message, 'type'))
# OBJECT_CLASS_SIGN
我收到了一个使用 Python 的 Google ProtoBuf,我正在尝试将枚举的值与它的字符串表示形式进行比较。基于 this and this 我应该能够使用类似 enum_values_by_name
的东西来获取我需要的信息。但是,所有 enum*
相关属性都是空的:
>>> type(my_message)
<class 'myObjects_pb2.myObject'>
>>> my_message
# nID: 53564
# nAge: 2
# type: OBJECT_CLASS_SIGN
# view: OBJECT_VIEW_FRONT
>>> my_message.type
# 1
>>> filter(lambda s: s.startswith('enum'), dir(my_message.DESCRIPTOR))
# ['enum_types', 'enum_types_by_name', 'enum_values_by_name']
>>> my_message.DESCRIPTOR.enum_types
# []
>>> my_message.DESCRIPTOR.enum_types_by_name
# {}
>>> my_message.DESCRIPTOR.enum_values_by_name
# {}
可能是因为我的protobuf定义在很多文件中,而我想要的enums在我导入的主文件中没有定义(但用于解码my_message
)?
为什么我得到这些空集合以及(更重要的)如何找到关于枚举的信息?
我不知道为什么消息的 DESCRIPTOR
包含未填充的枚举属性。 (这对我来说似乎是一个错误。)但是,(至少)有两个解决方案:
1) 如果您知道定义枚举的文件的名称,则可以 'hack' 通过以下方式按名称获取枚举值:
# This is the root ProtoBuf definition
from mydir import rootapi_pb2
# We happen to know that the enums are defined in myenums.proto
enum_file = rootapi_pb2.myenums__pb2 # NOTE: Extra Underscore!
enum_value = getattr(enum_file, 'OBJECT_CLASS_SIGN')
但是,如果您不想依赖这个 hack,您最终可以通过以下方式找到枚举描述符,从而从名称中找到值:
my_message.DESCRIPTOR.fields_by_name['type'].enum_type.values_by_name['OBJECT_CLASS_SIGN'].number
因为这太可怕了,所以它被包装成一个安全的、可重复使用的函数:
def enum_value(msg, field_name, enum_name):
"""Return the integer for the enum name of a field,
or None if the field does not exist, is not an enum, or the
enum does not have a value with the supplied name."""
field = msg.DESCRIPTOR.fields_by_name.get(field_name,None)
if field and field.enum_type:
enum = field.enum_type.values_by_name.get(enum_name,None)
return enum and enum.number
print(enum_value(my_message, 'type', 'OBJECT_CLASS_SIGN'))
# 1
python 的 ProtoBuf 非常丑陋...但我还是不得不使用它...
我觉得@Phrogz 的功能可以稍微简化一下。这是我附带的功能:
def get_enum_name_by_number(parent, field_name):
field_value = getattr(parent, field_name)
return parent.DESCRIPTOR.fields_by_name[field_name].enum_type.values_by_number.get(field_value).name
print(my_message.type)
# 1
print(get_enum_name_by_number(my_message, 'type'))
# OBJECT_CLASS_SIGN