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

See https://docs.python.org/3/library/enum.html#enum.auto

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

See https://docs.python.org/3/library/enum.html#enum.unique

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

See https://docs.python.org/3/library/enum.html#enum.Flag

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"