Add extra attributes to enum members in Python

Redowan Delowar February 17, 2022
Source

While grokking the source code of the http.HTTPStatus module, I came across this technique to add extra attributes to the values of enum members. Now, to understand what do I mean by adding attributes, let's consider the following example:

Here, I've inherited from str to ensure that the values of the enum members are strings. This class can be used as follows:

Running the script will print:

While this works but it's evident that you can only assign a single value to an enum member. How'd you rewrite this if you needed multiple values attached to a single enum member?

Suppose, in the above case, along with the color title, you also need to save the hex codes and short descriptions of the colors. One way you can achieve this is via the assignment of an immutable container as the value of an enum member:

Here, I'm using a tuple to contain the title, hex code, and description of the Color members. This gets awkward whenever you'll need to access the individual elements inside the tuple. You'll have to use hardcoded indexes to access the elements of the tuple. This is how you'll probably use it:

It prints:

Hardcoding indexes in such a manner is fragile and will break if you drop a new value in the middle of the tuple assigned to an enum member. Also, it's hard to reason through logic when you've to keep the semantic meanings of the index positions in your working memory. A better thing to do is to rewrite the enum in a way that'll allow you to access different elements of the member values by their attribute names. Let's do it:

Here, I overrode the new method of the class Color. Method new is a special class method that you don't need to decorate with the @classmethod decorator. It gets executed during the creation of the Color object; before the init method. Other than the first argument cls, you can define the new method with any number of arbitrarily named arguments.

In this case, the value of each member of Color will have three elements - title, hex_code, and description. So, I defined the new method to accept those arguments. In the following line, the str class was initialized via obj = str.new(cls, title) and then title was assigned to the newly created string object via obj.value=title. This line is crucial; without it, the enum won't operate at all. This assignment makes sure that the Enum.member.value will return a string value.

In the next two lines, hex_code and description were attached to the member values via the obj.hex_code=hexcode and obj.description=description statements respectively.

Now, you'll be able to use this enum without any hardcoded shenanigans:

This will print:

Discussion in the ATmosphere

Loading comments...