This article gives an overview of the tools available in the module enum
and how to use them.
enum
overviewThe standard library module enum
is used to create enumerations, objects that make it convenient to work with groups of related constant values.
Enumerations are also commonly used when you need a programmatic way to distinguish between different values without caring about the concrete values. This is often the case when you have functions that accept Booleans, integers, or string values, as configuration options for those functions.
This article provides an overview of the module enum
and the tools it provides so that you can work with enumerations ergonomically.
The article is structured in four sections, ordered from the most fundamental and commonly-useful tools of the module enum
to the most rarely-needed tools:
Enum
and auto
.StrEnum
, a very common type of specialise enumeration.Enum
and auto
Consider the signature for the function below, that connects to a weather API to fetch the temperature at a given location:
def get_temperature(city, unit):
...
print(get_temperature("Lisbon", "celsius")) # 18.0
print(get_temperature("Lisbon", "fahrenheit")) # 64.4
The parameter unit
can take one of two values and it specifies the unit in which the temperature is returned:
Value | Unit of the result |
---|---|
"celsius" |
Celsius |
"fahrenheit" |
Fahrenheit |
Working with arbitrary, loose string values works, but has a few issues:
It's because of situations like this that enumerations exist:
To create an enumeration, you inherit from the class Enum
from the module enum
1.
Then, you create a class variable for each different value that you want to use:
from enum import Enum
class TempUnit(Enum):
CELSIUS = "celsius"
FAHRENHEIT = "fahrenheit"
def get_temperature(city, unit):
...
print(get_temperature("Lisbon", TempUnit.CELSIUS)) # 18.0
print(get_temperature("Lisbon", TempUnit.FAHRENHEIT)) # 64.4
The enumeration members are the singleton values that you can use.
In this case, they're TempUnit.CELSIUS
and TempUnit.FAHRENHEIT
.
The members' names are typically written all in uppercase because they're constants: you won't change their values during the execution of the program.
Enumeration members know their own name and associated value, which you can access with the attributes name
and value
:
print(TempUnit.CELSIUS) # TempUnit.CELSIUS
print(repr(TempUnit.CELSIUS)) # <TempUnit.CELSIUS: 'celsius'>
print(TempUnit.CELSIUS.name) # CELSIUS
print(TempUnit.CELSIUS.value) # celsius
You'd need this, for example, if the API in question required the unit as a query parameter in the request URL:
def get_temperature(city, unit):
req_url_parameters = f"?city={city}&temp={unit.value}"
print(req_url_parameters)
...
get_temperature("Lisbon", TempUnit.CELSIUS) # ?city=Lisbon&temp=celsius
auto
In many scenarios, enumerations are good enough if their members map to different successive integers.
For example, it's easy to look at the enumeration Weekday
from the snippet below and image that it might be useful in programs that work with calendar dates, scheduling, and similar situations:
from enum import Enum
class Weekday(Enum):
SUNDAY = 1
MONDAY = 2
TUESDAY = 3
WEDNESDAY = 4
THURSDAY = 5
FRIDAY = 6
SATURDAY = 7
In cases where you want enumeration members to be numbered successively, starting at 1, you can use the shortcut auto
:
from enum import Enum, auto
class Weekday(Enum):
SUNDAY = auto()
MONDAY = auto()
TUESDAY = auto()
WEDNESDAY = auto()
THURSDAY = auto()
FRIDAY = auto()
SATURDAY = auto()
This might feel like a bad deal because you're typing more but auto
is more robust.
When numbering options by hand you might accidentally skip a value, repeat a value, or forget to update the values when you delete/reorder members.
With auto
, none of that happens.
auto
will assign a unique integer for each member, starting at 1
and increasing successively:
print(Weekday.SUNDAY.value) # 1
print(Weekday.SATURDAY.value) # 7
Enumerations provide some additional niceties to make it easier to work with them. The class of an enumeration can be queried for the number of members it contains and you can iterate over it to check all the members and their values:
print(len(TempUnit)) # 2
print(list(TempUnit))
# [<TempUnit.CELSIUS: 'celsius'>, <TempUnit.FAHRENHEIT: 'fahrenheit'>]
print(len(Weekday)) # 7
print(list(Weekday))
# [<Weekday.SUNDAY: 1>, <Weekday.MONDAY: 2>, <Weekday.TUESDAY: 3>,
# <Weekday.WEDNESDAY: 4>, <Weekday.THURSDAY: 5>, <Weekday.FRIDAY: 6>,
# <Weekday.SATURDAY: 7>]
You can also fetch an enumeration member programmatically from its name or from its value, respectively by indexing into the enumeration class with the member name or by instantiating it with the member value:
# Access the member by name:
print(Weekday["SATURDAY"]) # Weekday.SATURDAY
# Access the member by value:
print(Weekday(7)) # Weekday.SATURDAY
However, the instantiation syntax might look misleading, given that enumeration members are singletons:
# a, b, c, and d, are all the same object.
a = Weekday.SATURDAY
b = Weekday["SATURDAY"]
c = Weekday(7)
d = Weekday(7)
assert a is b
assert b is c
assert c is d
String enumerations are enumerations for which the members' values are strings.
from enum import StrEnum
class TempUnit(StrEnum):
CELSIUS = "celsius"
FAHRENHEIT = "fahrenheit"
The difference between a string enumeration (inheriting from StrEnum
) and an enumeration that uses string values (like the initial version of TempUnit
, that inherited from Enum
) is that the members of a string enumeration can be used directly as strings:
print(TempUnit.CELSIUS.capitalize()) # Celsius
print("Degrees " + TempUnit.CELSIUS) # Degrees celsius
print(TempUnit.FAHRENHEIT.split("e")) # ['fahr', 'nh', 'it']
If you use a regular enumeration that inherits from Enum
, these operations could only be performed on the values of the enumeration members and not on the members directly:
from enum import Enum
class TempUnitEnum(Enum):
CELSIUS = "celsius"
FAHRENHEIT = "fahrenheit"
# The value is still a string:
print(TempUnitEnum.FAHRENHEIT.value.split("e")) # ['fahr', 'nh', 'it']
# The member itself can't be treated like a string:
print(TempUnitEnum.FAHRENHEIT.split("e"))
# AttributeError: 'TempUnitEnum' object has no attribute 'split'
When used within a string enumeration, auto
will generate a member value that is equal to the member name in lowercase letters.
Hence, the version of the string enumeration TempUnit
seen above could also be defined as
from enum import StrEnum, auto
class TempUnit(StrEnum):
CELSIUS = auto()
FAHRENHEIT = auto()
assert TempUnit.CELSIUS.value == "celsius"
assert TempUnit.FAHRENHEIT.value == "fahrenheit"
The class StrEnum
, used for string enumerations, was added to the module enum
in Python version 3.11.
In earlier versions of Python, you can achieve a similar behaviour by inheriting from str
and Enum
:
from enum import Enum, auto
class TempUnitKindaStr(str, Enum):
CELSIUS = "celsius"
FAHRENHEIT = "fahrenheit"
print(TempUnitKindaStr.FAHRENHEIT.split("e")) # ['fahr', 'nh', 'it']
However, note that auto
will create integer member values, since the enumeration inherits from Enum
:
from enum import Enum, auto
class TempUnitKindaStr(str, Enum):
CELSIUS = auto()
FAHRENHEIT = auto()
print(TempUnitKindaStr.FAHRENHEIT.value) # 2
The class IntEnum
creates enumerations in which the members can be used directly as integers.
However, the result of such an operation will never be an enumeration member, even if the result is an integer that is the value of one of the members:
from enum import IntEnum
class SomeIntegers(IntEnum):
ONE = 1
TWO = 2
THREE = 3
print(repr(SomeIntegers.TWO)) # <SomeIntegers.TWO: 2>
print(repr(SomeIntegers.ONE + SomeIntegers.ONE)) # 2
Members of an enumeration created with the class IntEnum
can be used wherever an integer can be used.
Unlike string enumerations with StrEnum
, integer enumerations have been around for a while and thus can be used in any Python 3 version.
Enumerations can also be used to represent sets of Boolean flags that can be manipulated with the Boolean operators AND (&
), OR (|
), XOR (^
), and INVERT (~
).
You can create flag enumerations with the class Flag
and member values should be powers of two:
from enum import Flag
class NotificationMethod(Flag):
IN_APP = 1
PUSH = 2
EMAIL = 4
SMS = 8
user_settings = NotificationMethod.IN_APP | NotificationMethod.PUSH
if NotificationMethod.EMAIL not in user_settings:
print("Email notifications are disabled.")
# Output: Email notifications are disabled.
If you inspect the variable user_settings
, you see that it shows how it's combining the members IN_APP
and PUSH
, and its current value is 3:
print(user_settings) # <NotificationMethod.IN_APP|PUSH: 3>
Instead of defining the powers of two by hand, it is much more convenient to set the member values automatically with auto
:
from enum import Flag, auto
class NotificationMethod(Flag):
IN_APP = auto()
PUSH = auto()
EMAIL = auto()
SMS = auto()
user_settings = NotificationMethod.IN_APP | NotificationMethod.PUSH
print(user_settings) # <NotificationMethod.IN_APP|PUSH: 3>
Additionally, if you would like to be able to use flag members as integers directly, the module enum
also provides a class IntFlag
.
The class IntFlag
is to Flag
what the class IntEnum
is to Enum
.
For an example of a flag enumeration in the real world, check re.RegexFlag
:
from re import RegexFlag
print(RegexFlag) # <flag 'RegexFlag'>
print(RegexFlag.ASCII | RegexFlag.MULTILINE) # re.ASCII|re.MULTILINE
Different enumeration members can have the same value, in which case the two (or more) members that share the same value are considered aliases of one another:
class EnumWithAliases(Enum):
ONE = 1
TWO = 2
THREE = 3
UNO = 1 # Spanish
UM = 1 # Portuguese
print(EnumWithAliases.ONE == EnumWithAliases.UNO) # True
When iterating over the enumeration class, you only get the unique members:
print(list(EnumWithAliases))
# [<EnumWithAliases.ONE: 1>, <EnumWithAliases.TWO: 2>, <EnumWithAliases.THREE: 3>]
If you want to check all the members your enumeration has, including aliases, use the dunder attribute __members__
, which maps member names to members:
print(EnumWithAliases.__members__)
# {
# 'ONE': <EnumWithAliases.ONE: 1>,
# 'TWO': <EnumWithAliases.TWO: 2>,
# 'THREE': <EnumWithAliases.THREE: 3>,
# 'UNO': <EnumWithAliases.ONE: 1>,
# 'UM': <EnumWithAliases.ONE: 1>,
# }
If you would like to explicitly disable aliases, you can use enum.verify
to ensure your enumeration only has unique values.
Flag enumerations allow you to define aliases to certain masks – or groups – of flags.
For example, consider the following flag Colour
, based on the RGB colour model:
from enum import Flag
class Colour(Flag):
RED = 1
GREEN = 2
BLUE = 4
Given these three flags, you may want to create aliases for BLACK
(the absence of colour) and WHITE
(a combination of all colours):
BLACK = Colour(0)
WHITE = Colour.RED | Colour.GREEN | Colour.BLUE # or Colour(7)
In the snippet above, the instantiation Colour(0)
is the alias for “no flags set”, which is what you'd get if you intersected two disjoin sets of flags, for example:
print(Colour.RED & Colour.GREEN) # Colour(0)
The alias WHITE
has the numeric value 7
, because it's what you get if you combine the values 1
, 2
, and 4
, respectively from the members RED
, GREEN
, and BLUE
:
print(WHITE.value) # 7
If you're going to be using these two aliases a lot, it may make sense to define them within the enumeration:
class Colour(Flag):
BLACK = 0
RED = 1
GREEN = 2
BLUE = 4
WHITE = 7
This makes it so that the aliases are used automatically when certain operations with your flag members result in values that match those aliases. Here are two examples:
print(Colour.RED & Colour.GREEN) # Colour.BLACK
print(Colour.RED | Colour.GREEN | Colour.BLUE) # Colour.WHITE
This section covers some convenience tools that the module enum
provides to make it more ergonomic to work with enumerations.
The module enum
provides a decorator unique
for enumerations that raises an exception if the enumeration contains any aliases.
In other words, unique
errors if the member values of an enumeration are not unique.
Going back to the enumeration EnumWithAliases
from the section on enumerations with aliases, wrapping that enumeration in the decorator unique
raises an exception ValueError
:
from enum import Enum, unique
@unique
class EnumWithAliases(Enum):
ONE = 1
TWO = 2
THREE = 3
UNO = 1 # Spanish
UM = 1 # Portuguese
"""Output:
ValueError: duplicate values found in <enum 'EnumWithAliases'>: UNO -> ONE, UM -> ONE
"""
As seen in the example above, the exception will let you know what are the aliases that are causing trouble.
In Python 3.11, the decorator verify
was added to the module enum
.
Like unique
, it's a decorator for enumeration classes and it can be used to enforce a number of restrictions on the enumeration that is decorated.
The decorator verify
works with the following options:
UNIQUE
– ensures that member values are unique (same as using unique
).CONTINUOUS
– ensures that the member values form a contiguous sequence without any missing values. (Only useful for enumerations with integer values.)NAMED_FLAGS
– ensures that any flag groups are only made up of named flags. (Only useful for flag enumerations.)The next subsections show examples of CONTINUOUS
and examples of NAMED_FLAGS
to make it easier to understand how they work.
verify(CONTINUOUS)
The check for continuity is helpful to make sure you didn't skip an integer value by mistake and this works even if the first value isn't 1
.
The class Nums
below passes the verification because it starts at 3
and ends at 6
and all integers in between (4
and 5
) are associated with members:
from enum import Enum, verify, CONTINUOUS
@verify(CONTINUOUS)
class Nums(Enum):
THREE = 3
FOUR = 4
FIVE = 5
SIX = 6
However, the class NumsHole
below doesn't because the value 5
is not assigned to any member:
from enum import Enum, verify, CONTINUOUS
@verify(CONTINUOUS)
class NumsHole(Enum):
THREE = 3
FOUR = 4
SIX = 6
"""Output:
ValueError: invalid enum 'NumsHole': missing values 5
"""
For flag enumerations, the verification CONTINUOUS
will only check for powers of two.
As such, the flag enumeration MyFlag
shown below passes the verification, even though no members are associated with the values 5
, 6
, or 7
:
from enum import Flag, verify, CONTINUOUS
@verify(CONTINUOUS)
class MyFlag(Flag):
C = 4
D = 8
However, the flag enumeration MyFlagHole
from the next snippet doesn't pass verification because the value 8
(\(2^3\)) is missing between 4
(\(2^2\)) and 16
(\(2^4\)).
from enum import Flag, verify, CONTINUOUS
@verify(CONTINUOUS)
class MyFlag(Flag):
C = 4
E = 16
ValueError: invalid flag 'MyFlag': missing values 8
verify(NAMED_FLAGS)
To better understand the role of NAMED_FLAGS
, first you need to understand that flag enumerations allow you to define aliases directly in the enumeration.
Read the previous section on flags with aliases if you need a refresher.
When using NAMED_FLAGS
you are only allowed to create aliases that are composed of members.
Going back to the flag Colour
defined before, imagine a scenario where you forget to define the member Colour.GREEN
:
from enum import Flag
class Colour(Flag):
BLACK = 0
RED = 1 # <- GREEN should go
BLUE = 4 # <- between these.
WHITE = 7
The alias WHITE
still works, and it's composed of the flags Colour.RED
, Colour.BLUE
, and Colour(2)
, since there is no concrete member with the value 2
:
print(purple in Colour.WHITE) # True
print(Colour.WHITE ^ purple) # Colour(2)
If you use the constraint NAMED_FLAGS
, creation of the flag Colour
would fail because the alias WHITE
uses Colour(2)
, which is not a named flag:
from enum import Flag, verify, NAMED_FLAGS
@verify(NAMED_FLAGS)
class Colour(Flag):
BLACK = 0
RED = 1 # <- GREEN should go
BLUE = 4 # <- between these.
WHITE = 7
ValueError: invalid Flag 'Colour': alias WHITE is missing value 0x2 [use enum.show_flag_values(value) for details]
Another useful decorator that the module enum
provides is global_enum
, which uses dark magic2 to export the enumeration members to the global scope.
This works with any enumeration, and the snippet below shows it used in a flag enumeration:
from enum import Flag, auto, global_enum
@global_enum
class Colour(Flag):
RED = 1
GREEN = 2
BLUE = 4
print(BLUE == Colour.BLUE) # True
This decorator was added in Python 3.11, so make sure you're running that version of Python or try implementing it yourself if you're not! That would be quite an exercise!
If you're using this in a package, you might want to add these global names to the list __all__
.
This article gave you an overview of some of the tools that are available in the module enum
and that should cover the most common use cases.
In a way, I tried to show you the “20% of the tools that will take care of 80% of the use cases”.
But I left out a lot of interesting things:
member
and nonmember
that can be used to force something to be (or not to be) a member of the enumeration;FlagBoundary
that lets you customise the behaviour of flag enumerations when dealing with values that are out of the range of the flag;EnumMeta
which is used by all enumeration classes and that allow you to do things like Enumeration[MEMBER_NAME]
;EnumDict
, which is a subclass of dict
used by the internals of enumerations;ReprEnum
, used by StrEnum
and IntEnum
, that makes it more convenient to mix in other types with enumerations;enum.property
, which is very similar to the built-in property
, that allows you to create member properties that have the same names as members themselves (it's what powers .value
and .name
);Use enumerations when you need to group various constants together or when you need to define sets of options for other functions, classes, and more.
The classes Enum
and StrEnum
(for Python 3.11+) will be what you'll be using most of the time, and if you use auto
to assign values automatically, it will be very difficult to mess it up.
The next cohort of the Intermediate Python Course starts soon.
Grab your spot now and learn the Python skills you've been missing!
enum
, http://docs.python.org/3/library/enum [last accessed 24-04-2025];