In this post, we’ll describe enumerations, another interesting data type that Python makes available to the developer as part of the standard library.
This is not a fixed, defined data type, but rather an approach to defining your own task-specific data types. In the context of object-oriented programming, we’ll discuss in depth how to create custom data types with any functionality. At this point, we'll jump ahead a bit and tackle creating our own enumeration types.
An enumeration type is a data type whose value range is a set of symbolic constants. An example of this is the (still imaginary) data type weekday, whose instances each represent one day of the week. With the means described up to this point, this data type can be implemented as an integer or a string, for example. Both variants assume an implicitly defined assignment of numbers or character strings to the abstract "day of the week" concept. But this kind of assignment poses some problems:
The enum module from the standard library provides the basic enum class for immutable enumeration types. In the following example, the already mentioned weekday data type is implemented as an enumeration type:
>>> import enum
>>> class Weekday(enum.Enum):
... Monday = 1
... Tuesday = 2
... Wednesday = 3
... Thursday = 4
... Friday = 5
... Saturday = 6
... Sunday = 7
...
For each day of the week, a symbolic constant is created in the weekday class derived from enum. Each constant is assigned an internal value. This value is an integer in this case, but it doesn’t have to be.
A concrete day of the week can now be created using the defined constant, the assigned internal value, or its name:
>>> Weekday.Saturday
<Weekday.Saturday: 6>
>>> Weekday(6)
<Weekday.Saturday: 6>
>>> Weekday["Saturday"]
<Weekday.Saturday: 6>
You can also see from these examples that the screen output of an enumeration value contains its symbolic name. This name can be accessed via the name attribute:
>>> Weekday.Saturday.name
'Saturday'
Enumeration values can only be compared with each other. In particular, comparison with the internal value evaluates to False:
>>> Weekday.Monday != Weekday.Tuesday
True
>>> Weekday.Monday == 1
False
Enumeration types allow for running through their values. Any aliases will be skipped (aliases are enumeration values that are assigned the same internal value as a previously defined enumeration value):
>>> for day in Weekday:
... print(day)
...
Weekday.Monday
Weekday.Tuesday
Weekday.Wednesday
Weekday.Thursday
Weekday.Friday
Weekday.Saturday
Weekday.Sunday
Since Python 3.6, the auto function is available, which can be used to define an enumeration type without assigning concrete internal values:
>>> class Weekday(enum.Enum):
... Monday = enum.auto()
... Tuesday = enum.auto()
... Wednesday = enum.auto()
... Thursday = enum.auto()
... Friday = enum.auto()
... Saturday = enum.auto()
... Sunday = enum.auto()
...
>>> Weekday.Monday
<Weekday.Monday: 1>
Python 3.6 introduced the Flag enumeration type, which defines the internal values so that the symbolic constants can be combined using bitwise operators. This is illustrated by the following example, in which the state of a traffic light system is represented by a combination of symbolic constants:
>>> class TrafficLight(enum.Flag):
... Red = enum.auto()
... Amber = enum.auto()
... Green = enum.auto()
...
With the help of the bitwise OR, two symbolic constants, also referred to as flags, can be combined:
>>> combined_state = TrafficLight.Red | TrafficLight.Amber
In our example, combined_state would describe a traffic light where the red and amber lights are currently on together.
The bitwise AND can be used to check whether a symbolic constant is included in a combination:
>>> combined_state & TrafficLight.Red
<TrafficLight.Red: 1>
>>> combined_state & TrafficLight.Green
<TrafficLight: 0>
The result of the bitwise AND can be interpreted as a truth value:
>>> bool(combined_state & TrafficLight.Red)
True
>>> bool(combined_state & TrafficLight.Green)
False
One advantage of the enum enumeration type is that the symbolic enumeration values are strictly separated from their internal representations. For this reason, the comparison between the symbolic value Weekday.Monday and the numeric value 1 always evaluates to False, even though the value 1 is the internal representation of Weekday.Monday in the enumeration.
Occasionally, however, this kind of comparability is explicitly desired. For this purpose, the enum module contains the IntEnum data type, which can be used in a similar way to enum. Enumeration values of an enumeration type that inherits from IntEnum can be used as if they were integers:
>>> class Weekday(enum.IntEnum):
... Monday = 1
... Tuesday = 2
... Wednesday = 3
... Thursday = 4
... Friday = 5
... Saturday = 6
... Sunday = 7
...
>>> Weekday.Monday < 10
True
>>> Weekday.Monday * 2
2
>>> Weekday.Monday + Weekday.Tuesday
3
In particular, comparisons and operations with values of different IntEnum enumerations are also permitted.
Enumerations give your code a vocabulary of its own: a variable holding Weekday.Saturday tells you exactly what it represents, both in your logic and in your debug output. When choosing a type, start with Enum, move to Flag when values need to be combined, and save IntEnum for cases where members have to behave like integers, such as passing values to an older API that expects numbers. That last option gives up some of the safety that makes enumerations useful, so use it deliberately. The class-based approach you used here will come back when you start building fully custom data types of your own.
Editor’s note: This post has been adapted from a section of the book Python 3: The Comprehensive Guide by Johannes Ernesti and Peter Kaiser. Johannes is a research scientist at DeepL. He is a graduate mathematician and received his doctorate in applied mathematics from the Karlsruhe Institute of Technology (KIT). Peter is a research scientist at DeepL. He has a degree in computer science and received a doctorate in humanoid robotics from the Karlsruhe Institute of Technology (KIT).
This post was originally published 9/2022.