在 Python 中像枚举一样创建 C

Creating C like Enum in Python

也许我已经找到了这个问题的答案,因为这是不可能的,但如果有一个漂亮的把戏……我洗耳恭听。我正在尝试在 python:

中重现以下 C 枚举列表
enum Id
{
   NONE = 0,
   HEARTBEAT, //0x1
   FLUID_TRANSFER_REQUEST,
   FLUID_TRANSFER_STATUS_MSG,
   FLUID_TRANSFER_ERROR_MSG,
   FLUID_TRANSFER_RESUME,
   EMERGENCY_STOP_MSG,
   LOG_MSG,
   VERSION_REQUEST,
   VERSION_RESPONSE,
   CHANNEL_INFORMATION_REQUEST,
   CHANNEL_INFORMATION_RESPONSE,
   TEST_REQUEST,
   LED_CONTROL_REQ,
   RESET_REQ,

   // Camera App Messages
   START_SENDING_PICTURES = 0x010000,
   STOP_SENDING_PICTURES,
   START_RECORDING_VIDEO_REQ,
   STOP_RECORDING_VIDEO_REQ,
   TAKE_PICTURE_REQ,
   SET_VOLUME_LIMIT,         
   VIDEO_FRAME_MSG,
   PICTURE_MSG,
   I_FRAME_REQUEST,
   CURRENT_VOLUME,
   START_ANALYZING_IMAGES_REQ,
   STOP_ANALYZING_IMAGES_REQ,
   SET_FILE_PATH,

   //Sensor Calibration
   VOLUME_REQUEST = 0x020000,
   START_CAL,
   CLI_COMMAND_REQUEST,
   CLI_COMMAND_RESPONSE,

   // File Mananger
   NEW_DELIVERY_REQ = 0x30000,
   GET_DELIVERY_FILE_REQ,
   GET_FILE_REQ,

   ACK_NACK,
   RESPONSE,

   LAST_ID
};

但是,我不想为列表指定每个值,因为它经常变化。由于我还在各个部分将其设置为新值,因此我无法使用自动编号方法(例如 VOLUME_REQUEST = 0x020000)。任何人都知道在 python 中重现 C 风格枚举的巧妙技巧,还是我坚持用困难的方式重现它?

也许你可以在 Python 3 中使用 this 的一些变体。对于 Python 2,我只是做了以下操作(带括号以避免难看的 \在每行的末尾):

(T_OR, T_AND, T_NOT,
 T_OPEN_PAREN, T_CLOSE_PAREN,
 T_EQUAL, T_UNEQUAL,
 ...
 T_DEFCONFIG_LIST, T_MODULES, T_VISIBLE) = range(0, 39)

要处理多个范围,只需使用多个单独的 range() 赋值。缺点是您必须明确指定最终值。

How can I represent an 'Enum' in Python? 中似乎也有一些更高级的方法,但上面的方法应该最接近 C 风格 enum,语法开销最小。

不是一个严肃的建议,但如果你喜欢神秘的代码和有问题的做法,那么下面的 hack 似乎也适用于 C 风格 enum(需要注意的是枚举器总是创建在模块作用域)。随意竖琴。 ;)

def create_var_range(first_val, *names):
    for name in names:
        globals()[name] = first_val
        first_val += 1

create_var_range(0,
                 "NONE",
                 "HEARTBEAT",
                 ...)

create_var_range(0x010000,
                 "START_SENDING_PICTURES",
                 "STOP_SENDING_PICTURES",
                 ...)

create_var_range() 遍历其可变参数名称列表并为每个变量创建一个变量,从 first_val 开始分配递增的整数值。它使用 globals() 函数,return 一个带有全局符号 table.

的字典

如果适用,请参阅 Python 3.4 的 Python documentation。我根据在这里找到的内容编写了代码。

这主要是一个 hack,因为我确信有更有效的方法来解决我所做的事情。

from enum import IntEnum
import re

class AutoNumber(IntEnum):
    def __new__(cls, *args):
        numberList = re.findall(r'\d+', str(cls._member_map_))
        if len(cls.__members__) > 0 and not args:
            prevMax = max(map(int, numberList))
            value = prevMax + 1
            print(format(value, '#04x'))
        else:
            value = args[0]
            print(format(value, '#04x'))

        integer = int.__new__(cls)
        integer._value_ = value
        return integer

class EnumClass(AutoNumber):
   NONE = 0
   HEARTBEAT = () # 0x1
   FLUID_TRANSFER_REQUEST = ()
   # ...

   # Camera App Messages
   START_SENDING_PICTURES = 0x010000
   STOP_SENDING_PICTURES = ()
   START_RECORDING_VIDEO_REQ = ()
   # ...

   # Sensor Calibration
   VOLUME_REQUEST = 0x020000
   START_CAL = ()
   # ...

   # File Mananger
   NEW_DELIVERY_REQ = 0x30000
   GET_DELIVERY_FILE_REQ = ()
   GET_FILE_REQ = ()
   # ...

这个 'enum' 输出有效:

0x00 # NONE
0x01
0x02
...
0x10000 # START_SENDING_PICTURES
0x10001
0x10002
...
0x20000 # VOLUME_REQUEST
0x20001
...
0x30000 # NEW_DELIVERY_REQ
0x30001
0x30002
...

请注意,输出是使用 print 语句创建项目时的输出,它用于调试目的,但这是我获取数据的最佳方式。

有一个新的 aenum library by the author of enum34,它有一些额外的好处(例如基于 class 的 NamedTupleConstant class)。

如果您使用 Python3,其中一个很酷的功能是内置的自动编号:

from aenum import Enum

class Id(Enum, start=0):
    #
    _auto_on_                   # needed since aenum 3.0
    #
    NONE  # 0x0
    HEARTBEAT  # 0x1
    FLUID_TRANSFER_REQUEST
    FLUID_TRANSFER_STATUS_MSG
    FLUID_TRANSFER_ERROR_MSG
    # ...
    # Camera App Messages
    START_SENDING_PICTURES = 0x010000
    STOP_SENDING_PICTURES
    START_RECORDING_VIDEO_REQ
    STOP_RECORDING_VIDEO_REQ
    # ...
    # Sensor Calibration
    VOLUME_REQUEST = 0x020000
    START_CAL
    CLI_COMMAND_REQUEST
    CLI_COMMAND_RESPONSE
    #
    # File Mananger
    NEW_DELIVERY_REQ = 0x30000
    GET_DELIVERY_FILE_REQ
    GET_FILE_REQ
    #
    ACK_NACK
    RESPONSE
    #
    LAST_ID

并在使用中:

print(repr(Id.HEARTBEAT))
# <Id.HEARTBEAT: 1>

print(repr(Id.STOP_SENDING_PICTURES))
# <Id.STOP_SENDING_PICTURES: 65537>

print(repr(Id.VOLUME_REQUEST))
# <Id.VOLUME_REQUEST: 131072>