Recently I was reading a post by Eli Bendersky (one of my favorite bloggers) and I ran across a sentence in which Eli says “It’s a shame Python still doesn’t have a functional enum type, isn’t it?”
The comment startled me because I had always thought that it was obvious how to do enums in Python, and that it was obvious that you don’t need any special language features to do it. Eli’s comment made me think that I might need to do a reality-check on my sense of what was and was not obvious about enums in Python.
So I googled around a bit and found that there are a lot of different ideas about how to do enums in Python. I found a very large set of suggestions on StackOverflow here and here and here. There is a short set of suggestion on Python Examples. The ActiveState Python Cookbook has a long recipe, and PEP-354 is a short proposal (that has been rejected). Surprisingly, I found only a couple of posts that suggested what had seemed to me to be THE obvious solution. The clearest was by snakile on StackOverflow.
Anyway, to end the suspense, the answer that seemed to me so obvious was this. An enum is an enumerated data type. An enumerated data type is a type, and a type is a class.
class Color : pass class Red (Color): pass class Yellow (Color): pass class Blue (Color): pass
Which allows you to do things like this.
class Toy: pass myToy = Toy() myToy.color = "blue" # note we assign a string, not an enum if myToy.color is Color: pass else: print("My toy has no color!!!") # produces: My toy has no color!!! myToy.color = Blue # note we use an enum print("myToy.color is", myToy.color.__name__) # produces: myToy.color is Blue print("myToy.color is", myToy.color) # produces: myToy.color is <class '__main__.Blue'> if myToy.color is Blue: myToy.color = Red if myToy.color is Red: print("my toy is red") # produces: my toy is red else: print("I don't know what color my toy is.")
So that’s what I came up with.
But with so many intelligent people all trying to answer the same question, and coming up with such a wide array of different answers, I had to fall back and ask myself a few questions.
- Why am I seeing so many different answers to what seems like a simple question?
- Is there one right answer? If so, what is it?
- What is the way — the best, or most widely-used, or most pythonic — way to do enums in Python?
- Is the question really as simple as it seems?
For me, the jury is still out on most of these questions, but until they return with a verdict I have come up with two thoughts on the subject.
First, I think that many programmers come to Python with backgrounds in other languages — C or C++, Java, etc. Their experiences with other languages shape their conceptions of what an enum — an enumerated data type — is. And when they ask “How can I do enums in Python?” they’re asking a question like the question that sparked the longest thread of answers on StackOverflow:
I’m mainly a C# developer, but I’m currently working on a project in Python. What’s the best way to implement the equivalent of an enum [i.e. a C# enum] in Python?
So naturally, the question “How can I implement in Python the equivalent of the kind of enums that I’m familiar with in language X?” has at least as many answers as there are values of X.
My second thought is somewhat related to the first.
Python developers believe in duck typing. So a Python developer’s first instinct is not to ask you:
What do you mean by “enum”?
A Python developer’s first instinct is to ask you:
What kinds of things do you think an “enum” should be able to do?
What kinds of things do you think you should be able to do with an “enum”?
And I think that different developers probably have very different ideas about what one should be able to do with an “enum”. Naturally, that leads them to propose different ways of implementing enums in Python.
As a simple example, consider the question — Should you be able to sort enums?
My personal inclination is to say that — in the most conceptually pure sense of “enum” — the concept of sorting enums makes no sense. And my suggestion for implementing enums in Python reflects this. Suppose you implement a “Color” enum using the technique that I’ve proposed, and then try to sort enums.
# how do enumerated values sort? colors = [Red, Yellow, Blue] colors.sort() for color in colors: print(color.__name__)
What you get is this:
Traceback (most recent call last): File "C:/Users/ferg_s/pydev/enumerated_data_types/edt.py", line 32, in <module> colors.sort() TypeError: unorderable types: type() < type()
So that suites me just fine.
But I can easily imagine someone (myself?) working with an enum for, say, Weekdays (Sunday, Monday, Tuesday… Saturday). And I think it might be reasonable in that situation to want to be able to sort Weekdays and to do greater than and less than comparisons on them.
So if we’re talking duck typing, I’m happy with enums/ducks that are motionless and silent. My only requirement is that they be different from everything else and different from each other. But I can easily imagine situations where one might reasonably need/want/prefer ducks that can form a conga line, dance, and sing a few bars. And for those situations, you obviously need more elaborate implementations of enums.
So, with these thoughts in mind, I’m inclined to think that there is no single, best way to implement an enum in Python. The concept of an enum is flexible enough to cover a variety of implementations offering a variety of features.