The quest for the perfect python enum/constant declaration, Part 1

Updated: python code in the following post.

As you may know, Python is such a great language: it is really well designed from the start! One example of this is that most common and useful data types are part of the main language (list, dictionaries, sets, you name it), instead of being an “add-on” or library implementation as seen in most programming languages (like collections in Java, and not to mention C and the lack of these).

However, one data type that developers always are missing is the C-equivalent enum.

An enum is a set of variables with usually integer values, a good example could be the log level for a logger implementation, like DEBUG=10, INFO=20, WARN=30, ERROR=40, FATAL=50 and NONE=0. This is implemented with the keyword enum in C, for example the possible states of the avahi server are declared this way:
typedef enum {
AVAHI_SERVER_INVALID, /** Invalid state (initial) */
AVAHI_SERVER_REGISTERING, /** Host RRs are being registered */
AVAHI_SERVER_RUNNING, /** All host RRs have been established */
AVAHI_SERVER_COLLISION, /** There is a collision with a host RR. All host RRs have been withdrawn, the user should set a new host name via avahi_server_set_host_name() */
AVAHI_SERVER_FAILURE /** Some fatal failure happened, the server is unable to proceed */
} AvahiServerState;

The python way to solve this problem is declaring variables using the range() function, a simple port to python idiom is the following example:
>>> class AvahiServerState:
... INVALID, REGISTERING, RUNNING, COLLISION, FAILURE = range(5)
...
>>> AvahiServerState.
AvahiServerState.COLLISION AvahiServerState.FAILURE AvahiServerState.INVALID AvahiServerState.REGISTERING AvahiServerState.RUNNING AvahiServerState.__doc__ AvahiServerState.__module__
>>> AvahiServerState.INVALID
0
>>> AvahiServerState.FAILURE
4

It may suit your needs, but is missing one important feature I always look in this kind of data type: an easy way of getting the name of the value. I mean:
# This would be nice :)
>>> print str(AvahiServerState.INVALID)
AvahiServerState.INVALID
# Instead of this :(
>>> print str(AvahiServerState.INVALID)
0

You can solve this by googling many receipes of python, most of them implement a class or a pair of classes. From the above example, is important to note that an enum in C is not intended for declaring a sequence of values, rather it is a way to declare constants as a type.

But now, there is a more common idiom in C for declaring constants: you just put #defines in a .h file, especially in bit-based flags, for example:
/* Owner flags */
#define DBUS_NAME_FLAG_ALLOW_REPLACEMENT 0x1 /** Allow another service to become the primary owner if requested */
#define DBUS_NAME_FLAG_REPLACE_EXISTING 0x2 /** Request to replace the current primary owner */
#define DBUS_NAME_FLAG_DO_NOT_QUEUE 0x4 /** If we can not become the primary owner do not place us in the queue */

Well, I have never found an elegant python declaration for this. In my dreams, you could do something like this:
>>> int(ALLOW_REPLACEMENT)
1
>>> int(ALLOW_REPLACEMENT|REPLACE_EXISTING)
3
>>> str(ALLOW_REPLACEMENT|REPLACE_EXISTING)
ALLOW_REPLACEMENT|REPLACE_EXISTING
>>> repr(ALLOW_REPLACEMENT|REPLACE_EXISTING)
ALLOW_REPLACEMENT(0x1)|REPLACE_EXISTING(0x2)

So, lets summarize what we really want:

  1. An easy way to declare a type with a set of related constants: a name and an (integer) value
  2. An easy way to convert a constant from its value to its name, and vice-versa.
  3. An easy way to declare sequence-based constants and bit-based flag constants.
  4. The type must not be immutable: new constants may be added later.

The last item is something I would really like to add. As I say, many times this constants are meant to be immutable; in other words, once the set of name/values is set you are not allowed to change it. That’s something I don’t like in a language like python, so we can add values later in the program (one example for this is reading user-defined LOG levels from a configuration file).

1 Response to “The quest for the perfect python enum/constant declaration, Part 1”


Leave a Reply