2

Maybe I have already found the answer to this question in that it's not possible, but if there's a nifty trick... I'm all ears. I'm trying to reproduce the following C enumeration list in python:

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
};

However, I don't want to have to specify every value for the list because it's changing often. Since I also have it set to a new value in the various sections, I can't use the AutoNumber methodology (e.g. VOLUME_REQUEST = 0x020000). Anyone got a clever trick to reproduce C style enums in python, or am I stuck with reproducing it the hard way?

ls6777
  • 386
  • 1
  • 5
  • 14
  • @David, I did see before I posted [How can I represent an 'Enum' in Python?](http://stackoverflow.com/questions/36932/how-can-i-represent-an-enum-in-python) but I didn't think it answered my specific request in a good way. I believe the answers below are much better served for this particular request. – ls6777 Mar 26 '15 at 20:56
  • The answers here are a subset of, and a pale imitation, of the answers there. I suggest that you read those answers more closely. – David Heffernan Mar 26 '15 at 20:58
  • I just went through those answers, and none of them address the OP's question. If you are curious about my credentials: I'm the primary author of the [Enum data type](http://docs.python.org/3.4/library/enum.html), and of the [enum34 backport](https://pypi.python.org/pypi/enum34). – Ethan Furman Mar 09 '16 at 09:19

3 Answers3

4

Maybe you could use some variant of this in Python 3. For Python 2, I simply did the following (with parentheses to avoid an ugly \ at the end of each line):

(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)

To handle multiple ranges, just use multiple separate range() assignments. The downside is that you have to specify the final value explicitly.

There seems to be some fancier methods in How can I represent an 'Enum' in Python? too, but the above should be closest to a C-style enum with minimal syntactic overhead.

Not a serious suggestion, but if you enjoy cryptic code and questionable practices, then the hack below seems to work as well for a C-style enum (with the caveat that the enumerators are always created at module scope). Feel free to harp on it. ;)

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() steps through its variable argument list of names and creates a variable for each, assigning increasing integer values starting from first_val. It makes use of the globals() function, which return a dictionary with the global symbol table.

Community
  • 1
  • 1
Ulfalizer
  • 4,664
  • 1
  • 21
  • 30
  • Thanks! I think this and @Saturisk answers will work for my purposes. I just need to pick which one I want to use :) – ls6777 Mar 25 '15 at 23:23
4

Please see the Python documentation for Python 3.4 if this is applicable. I wrote my code based off of what I found here.

This is mostly a hack because I'm sure there is a much more efficient way to solve what I have done.

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 = ()
   # ...

This 'enum' outputs are effectively:

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

Note, the output is when the item is created using the print statement, it was meant for debug purposes, but it's the nicest way I could get the data to come out.

Ethan Furman
  • 63,992
  • 20
  • 159
  • 237
Shawnic Hedgehog
  • 2,353
  • 1
  • 15
  • 20
2

There is a new aenum library by the author of enum34 which has a few extra goodies (such as a class-based NamedTuple and a Constant class).

One of the cool features available if you are using Python 3 is the built-in auto-numbering:

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

and in use:

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 Furman
  • 63,992
  • 20
  • 159
  • 237
  • This is pretty cool and may be the cleanest, simplest solution, but I didn't have the library at the time. And I believe we were limited to what external libraries we could include. Nice solution though @Ethan :) – ls6777 Oct 12 '16 at 22:02
  • 1
    @ls6777: Thanks! If you like it, it's okay to up-vote it. ;) – Ethan Furman Oct 12 '16 at 22:07
  • I did upvote, but it won't publicly display until I have 15 rep points ;) – ls6777 Oct 14 '16 at 17:15
  • 1
    Ah, okay. Thanks! – Ethan Furman Oct 14 '16 at 17:25
  • @roberto: More details, please -- I just tested with the latest versions of aenum and Python and it worked fine. – Ethan Furman Nov 25 '20 at 15:49
  • yes it runs, but i was confused by the fact that pycharm underlines in red indicating 'Unresolved ref NONE' and all the rest – roberto Nov 27 '20 at 07:22
  • @roberto: Thank you, that's a much more useful comment. And you're right -- linters, etc., will not be happy with the above code, or other code shenanigans (such as modifying `globals()` to create variables). – Ethan Furman Nov 27 '20 at 20:05