ConstantDict, Yet Another Python Enumeration Pattern
Introduction
The Python language does not natively provide enumerations. Rejected PEP 354 proposed a string-constructor-based enumeration, later provided as the separate enum package. The web is rife with various cookbook recipes and ad-hoc solutions – see, e.g., ActiveState and Stack Overflow.
Here is ConstantDict
, one of my preferred enumeration patterns.
ConstantDict
This pattern (like most Python enumeration recipes) is quite simple and straightforward, so I’ll actually start with the enumeration class, and then discuss the merits / drawbacks after.
The enumeration class is as follows:
Essentially, we create an internal dictionary of all class variables that are upper-cased (well, and methods too), and wrap up a couple of instance built-in methods. Implementing an enumeration with the pattern is pretty straightforward. Here is a “days of the week” enumeration contrived by multiply inheriting two base enumeration classes:
And a simple use case example:
Discussion
The ConstantDict
pattern shines in a couple of key areas:
- It has a terse, clean, and extensible structure. The enumerations are inheritable, and even support multiple inheritance (as the above examples shows). Also, the enumerations can be enhanced with helper methods, etc., because they are real classes.
- The structure allows arbitrary enumeration values. In my example I used strings, but anything (even including functions) are valid values.
- The enumeration members are real class variables and not strings. For me this
is a key point over a pure string-based solution, as I want to be able to run
pylint
and have it throw errors on invalid enumeration names.
However, ConstantDict
probably isn’t the best choice for many situations.
Some of the drawbacks of the pattern include:
- Enumeration members are unordered. Because everything is shoved into a dictionary, there are no order guarantees.
- Despite being called
ConstantDict
, the enumeration values aren’t actually enforced constants. - Enumeration values must be manually specified and are not guaranteed to be unique. As noted above arbitrary (including identical) enumeration values are allowed. However, the pattern could be easily extended to raise an exception on detection of duplicate values.
- An enumeration class cannot have other upper-cased class variables or members. This should not be too big of a deal as the enumeration class is ideally used just as a small constant holder and not a multi-purpose class.
- The caller has to instantiate an instance of the
ConstantDict
class. This is necessary for the__contains__
and__iter__
convenience method. In the past, I have used a different version of theConstantDict
class that is used only at the class variable / method level.
There are certainly other tweaks and enhancements that can make the
ConstantDict
pattern better for a given situation. But, in its most basic
form as described above, it provides an easy, extensible thin-wrapper
enumeration.