Some tips on Python’s enums
Posted on 2025-04-12 in Programmation
I’d like to share a few tips I recently (re)discovered about Python’s enums. While interesting on its own, this article is also paving the way on a more advanced article I hope to write soon. Let’s dive right in!
auto
By using the function enum.auto, you can set integer values automatically in you enum. So this:
class MyEnum(enum.IntEnum): FIRST = enum.auto() SECOND = enum.auto()
is equivalent to this:
class MyEnum(enum.IntEnum): FIRST = 1 SECOND = 2
unique
Utility decorators to make sure all values are unique in the enum. So this will raise ValueError: duplicate values found in <enum 'MyEnum'>: THIRD -> SECOND:
@enum.unique class MyEnum(enum.IntEnum): FIRST = 1 SECOND = 2 THIRD = 2
nonmember
This function will prevent a field to become an enum member. It will be a standard attribute instead. So this code:
class MyEnum(enum.IntEnum): FIRST = 1 SECOND = 2 my_field = enum.nonmember(5) print(repr(MyEnum.FIRST)) print(repr(MyEnum.my_field))
will print:
<MyEnum.FIRST: 1> 5
See https://docs.python.org/3/library/enum.html#enum.nonmember
enum.Flag
A utility class to create an enum that supports bitwise operations.
class Permission(enum.Flag): READ = enum.auto() WRITE = enum.auto() read_or_write = Permission.READ | Permission.WRITE
enum.verify
A decorator to apply various predefined verifications on your enums. You can check that your values are unique (like with enum.unique), continuous or valid flags. For instance, this code will raise ValueError: invalid enum 'MyEnum': missing values 2.
@enum.verify(enum.CONTINUOUS) class MyEnum(enum.IntEnum): FIRST = 1 THIRD = 3
See https://docs.python.org/3/library/enum.html#enum.verify and https://docs.python.org/3/library/enum.html#enum.EnumCheck
Extra
Sadly, the provided verify decorator can only run pre-defined checks. Luckily, you can get inspiration from it to create your own. For instance, to check that all members are of the same type and raise a ValueError if not, you can use:
def enforce_member_type(type_to_enforce: type): def wrapper(cls): for member in cls: if isinstance(member.value, type_to_enforce): continue raise ValueError(f"{member} is not of type {type_to_enforce.__name__}") return cls return wrapper @enforce_member_type(int) class WrongTypeInEnforcedEnum(enum.Enum): FIRST = 1 SECOND = "second"