【问题标题】:Creating C like Enum in Python在 Python 中创建类似 C 的枚举
【发布时间】:2015-03-25 19:36:05
【问题描述】:

也许我已经找到了这个问题的答案,因为这是不可能的,但如果有一个绝妙的技巧......我全神贯注。我正在尝试在 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 样式枚举的聪明技巧,还是我坚持以困难的方式重现它?

【问题讨论】:

  • @David,我在发布How can I represent an 'Enum' in Python? 之前确实看到过,但我认为它没有很好地回答我的具体要求。我相信下面的答案更适合这个特定的请求。
  • 这里的答案是那里答案的一个子集,并且是一个苍白的模仿。我建议您更仔细地阅读这些答案。
  • 我刚刚浏览了这些答案,但没有一个能解决 OP 的问题。如果您对我的凭据感到好奇:我是 Enum data typeenum34 backport 的主要作者。

标签: python c enums


【解决方案1】:

也许您可以在 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() 函数,该函数返回一个带有全局符号表的字典。

【讨论】:

  • 谢谢!我认为这和@Saturisk 的答案将适用于我的目的。我只需要选择我想使用的那个:)
【解决方案2】:

如果适用,请参阅 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 = ()
   # ...

这个“枚举”输出是有效的:

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

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

【讨论】:

  • 我更新了工作代码。这主要是一种 hack,但可以有效地作为一个自我更新的枚举。
  • 我不是每个枚举后 = () 的忠实粉丝,但这可以完成我想要的工作。谢谢!
【解决方案3】:

enum34 的作者有一个新的aenum library,它有一些额外的好处(例如基于类的NamedTupleConstant 类)。

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

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>

【讨论】:

  • 这很酷,可能是最干净、最简单的解决方案,但我当时没有库。而且我相信我们仅限于我们可以包含的外部库。不错的解决方案虽然@Ethan :)
  • @ls6777:谢谢!如果你喜欢它,可以投票给它。 ;)
  • 我投了赞成票,但在我获得 15 个代表点之前它不会公开显示 ;)
  • 啊,好吧。谢谢!
  • @roberto:请提供更多详细信息 -- 我刚刚使用最新版本的 aenum 和 Python 进行了测试,效果很好。
猜你喜欢
  • 2022-01-09
  • 2012-09-23
  • 2020-03-03
  • 2012-07-04
  • 1970-01-01
  • 1970-01-01
  • 2021-09-08
  • 1970-01-01
相关资源
最近更新 更多