Python - Python enum Module and Enumeration Types

Introduction

In Python, the enum module provides a way to create enumeration types, commonly called enums. An enumeration is a collection of named constants that represent a fixed set of related values. Instead of using unrelated numbers or strings throughout a program, developers can give meaningful names to those values.

For example, suppose an application represents the days of a week. Using numbers such as 1, 2, and 3 may make the code difficult to understand. With an enumeration, we can define MONDAY, TUESDAY, and WEDNESDAY as named members. This makes programs easier to read, maintain, and debug.

Python's enum module is available as part of the standard library, so it does not require an external package.

Importing the enum Module

The most commonly used class from the module is Enum.

from enum import Enum

We can then create an enumeration by defining a class that inherits from Enum.

from enum import Enum

class Color(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3

Here, Color is an enumeration and RED, GREEN, and BLUE are its members.

Each member has both a name and a value.

print(Color.RED)
print(Color.RED.name)
print(Color.RED.value)

Output:

Color.RED
RED
1

The name is RED, while its associated value is 1.

Why Use Enumerations?

Enums are useful when a variable should contain one value from a predefined collection of choices.

For example, an employee management application might have different employee statuses:

from enum import Enum

class EmployeeStatus(Enum):
    ACTIVE = "active"
    INACTIVE = "inactive"
    ON_LEAVE = "on_leave"

Instead of repeatedly writing strings such as "active" or "inactive", the program can use:

EmployeeStatus.ACTIVE

This makes the intention of the code clearer.

Enums are particularly useful when:

  • A program has a fixed set of choices.

  • Values have meaningful names.

  • Developers want to avoid arbitrary constants.

  • Code readability is important.

  • The same set of values is used in multiple places.

Accessing Enum Members

Members can be accessed using the class name and member name.

from enum import Enum

class Direction(Enum):
    NORTH = 1
    SOUTH = 2
    EAST = 3
    WEST = 4

print(Direction.NORTH)
print(Direction.SOUTH)

The output is:

Direction.NORTH
Direction.SOUTH

The value of an enum member can be obtained using .value.

print(Direction.NORTH.value)

Output:

1

The name can be obtained using .name.

print(Direction.NORTH.name)

Output:

NORTH

Iterating Over an Enum

Enum members can be iterated over using a for loop.

from enum import Enum

class Season(Enum):
    SPRING = 1
    SUMMER = 2
    AUTUMN = 3
    WINTER = 4

for season in Season:
    print(season)

Output:

Season.SPRING
Season.SUMMER
Season.AUTUMN
Season.WINTER

To display both names and values:

for season in Season:
    print(season.name, season.value)

Output:

SPRING 1
SUMMER 2
AUTUMN 3
WINTER 4

This is useful when an application needs to process every possible option in an enumeration.

Comparing Enum Members

Enum members can be compared with one another.

from enum import Enum

class Status(Enum):
    SUCCESS = 1
    FAILURE = 2

print(Status.SUCCESS == Status.SUCCESS)
print(Status.SUCCESS == Status.FAILURE)

Output:

True
False

An important point is that enum members are not simply ordinary integers or strings.

For example:

class Status(Enum):
    SUCCESS = 1

Then:

Status.SUCCESS == 1

is False.

This distinction helps prevent accidental comparisons between unrelated types.

Enum Values Can Be Strings

Enum values do not have to be integers. Strings can also be used.

from enum import Enum

class Language(Enum):
    PYTHON = "Python"
    JAVA = "Java"
    CPP = "C++"

We can access the values as follows:

print(Language.PYTHON.value)

Output:

Python

String-based enums are particularly useful when working with configuration values, API responses, database fields, or application states.

Creating an Enum from a Value

Python allows us to retrieve an enum member using its value.

from enum import Enum

class Priority(Enum):
    LOW = 1
    MEDIUM = 2
    HIGH = 3

priority = Priority(2)

print(priority)

Output:

Priority.MEDIUM

This is useful when a program receives a value from an external source and needs to convert it into a meaningful enum member.

If the value does not exist, Python raises a ValueError.

Priority(5)

This produces an error because 5 is not associated with any member of Priority.

Enum Aliases

Python allows multiple enum names to have the same value.

from enum import Enum

class Status(Enum):
    SUCCESS = 1
    COMPLETED = 1
    FAILURE = 2

Here, SUCCESS and COMPLETED have the same value. COMPLETED becomes an alias of SUCCESS.

print(Status.SUCCESS)
print(Status.COMPLETED)

Both refer to the same enum member.

To obtain only the unique members and ignore aliases, iteration over the enum is useful.

Automatically Assigning Values with auto()

Python provides auto() for automatically generating enum values.

from enum import Enum, auto

class TaskStatus(Enum):
    PENDING = auto()
    RUNNING = auto()
    COMPLETED = auto()
    FAILED = auto()

Python automatically assigns values to these members.

This is useful when the exact numeric values are not important and only the uniqueness of the members matters.

IntEnum

Python also provides IntEnum. It is similar to Enum, but its members are also instances of int.

from enum import IntEnum

class Priority(IntEnum):
    LOW = 1
    MEDIUM = 2
    HIGH = 3

Because Priority inherits from IntEnum, its members can participate in integer-oriented operations and comparisons.

print(Priority.HIGH == 3)

Output:

True

IntEnum can be useful when working with older systems, APIs, or libraries that expect integer values while the application still benefits from meaningful enum names.

However, ordinary Enum should generally be preferred when integer compatibility is not specifically required because it provides stronger separation between the enumeration and raw integers.

StrEnum

Modern versions of Python also provide StrEnum, which is designed for string-based enumeration members.

from enum import StrEnum

class Environment(StrEnum):
    DEVELOPMENT = "development"
    TESTING = "testing"
    PRODUCTION = "production"

This is useful when enum members need to behave naturally as strings while still providing named constants.

The availability of StrEnum depends on the Python version, so code intended for older Python versions may use other approaches.

Flag and IntFlag

The enum module also includes Flag and IntFlag. These are useful when multiple options need to be combined using bitwise operations.

For example:

from enum import Flag, auto

class Permission(Flag):
    READ = auto()
    WRITE = auto()
    EXECUTE = auto()

Permissions can then be combined:

user_permission = Permission.READ | Permission.WRITE

The resulting value represents both permissions.

This approach is commonly useful for permission systems, feature flags, configuration options, and other situations where multiple independent choices can be enabled simultaneously.

Using Enums in Conditional Statements

Enums can make conditional logic easier to understand.

from enum import Enum

class TrafficLight(Enum):
    RED = 1
    YELLOW = 2
    GREEN = 3

light = TrafficLight.GREEN

if light == TrafficLight.RED:
    print("Stop")
elif light == TrafficLight.YELLOW:
    print("Prepare to stop")
elif light == TrafficLight.GREEN:
    print("Go")

The code is more descriptive than checking unexplained numeric values such as 1, 2, and 3.

Enums in Functions

Enums can also be passed to functions.

from enum import Enum

class PaymentStatus(Enum):
    PENDING = 1
    PAID = 2
    FAILED = 3

def process_payment(status):
    if status == PaymentStatus.PAID:
        print("Payment completed")
    elif status == PaymentStatus.PENDING:
        print("Payment is pending")
    elif status == PaymentStatus.FAILED:
        print("Payment failed")

process_payment(PaymentStatus.PAID)

This improves the readability of function calls because the argument clearly communicates its intended meaning.

Adding Methods to an Enum

An enum is a class, so methods can be defined inside it.

from enum import Enum

class OrderStatus(Enum):
    PENDING = 1
    SHIPPED = 2
    DELIVERED = 3

    def is_completed(self):
        return self == OrderStatus.DELIVERED

We can use the method like this:

status = OrderStatus.DELIVERED

print(status.is_completed())

Output:

True

This allows related behavior to be kept together with the enumeration.

Advantages of Python Enums

Enums provide several advantages.

Improved readability: Meaningful names such as PaymentStatus.PAID are easier to understand than arbitrary values such as 2.

Reduced errors: Using predefined members can prevent accidental use of unsupported values.

Better maintainability: If the available choices change, they can be managed centrally in the enum definition.

Clearer code: Enums communicate the expected set of values directly.

Reusable constants: The same enum can be used throughout multiple functions and classes.

Type distinction: Standard Enum members are distinct from their underlying values, which can prevent unintended comparisons.

Important Considerations

Enums are most appropriate when the possible values form a relatively fixed set. They should not be used simply because a program contains constants.

For example, if an application has hundreds of values that change frequently, an enum may not be the most suitable solution.

It is also important to distinguish between Enum, IntEnum, Flag, and IntFlag. Standard Enum provides stronger separation from ordinary values, while the integer-based variants are useful when compatibility with integer operations is required.

Practical Example

Consider an online order management system:

from enum import Enum

class OrderStatus(Enum):
    PLACED = "placed"
    PROCESSING = "processing"
    SHIPPED = "shipped"
    DELIVERED = "delivered"
    CANCELLED = "cancelled"

def display_status(status):
    if status == OrderStatus.PLACED:
        print("Order has been placed.")
    elif status == OrderStatus.PROCESSING:
        print("Order is being processed.")
    elif status == OrderStatus.SHIPPED:
        print("Order has been shipped.")
    elif status == OrderStatus.DELIVERED:
        print("Order has been delivered.")
    elif status == OrderStatus.CANCELLED:
        print("Order has been cancelled.")

current_status = OrderStatus.SHIPPED

display_status(current_status)

Output:

Order has been shipped.

Here, the program has a clearly defined set of valid order statuses. Instead of passing arbitrary strings throughout the application, the OrderStatus enum provides a controlled and readable representation of these states.

Conclusion

Python's enum module is an effective way to represent a fixed collection of related named values. It provides more structure and clarity than using unrelated integer or string constants. Developers can access enum members by name, retrieve their values, iterate through them, compare them, and use them in functions and conditional statements.

The module also provides specialized types such as IntEnum, StrEnum, Flag, and IntFlag for situations where integer, string, or combination-based behavior is required. By choosing the appropriate enumeration type, Python programs can become more readable, maintainable, and less prone to errors.