How to use enums in Python

Learn how to use enums in Python. This guide covers different methods, tips, real-world applications, and how to debug common errors.

How to use enums in Python
Published on: 
Tue
Mar 10, 2026
Updated on: 
Fri
Mar 13, 2026
The Replit Team

Python's enum module lets you create symbolic names for constants. This improves code readability and maintainability. It's a powerful tool to manage sets of related, unchangeable values.

Here, you'll learn essential techniques and tips to use enums effectively. You'll also find real-world applications and debugging advice to help you master this feature in your projects.

Creating a basic Enum class

from enum import Enum

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

print(Color.RED)
print(Color.RED.name, Color.RED.value)--OUTPUT--Color.RED
RED 1

To create an enumeration, your class must inherit from Enum. In the Color class, RED, GREEN, and BLUE are members, each representing a constant. Accessing Color.RED gives you the enum member itself, not just the integer 1.

This distinction is important. Each member has two key attributes you can access:

  • .name: The member's name as a string (e.g., 'RED').
  • .value: The value assigned to the member (e.g., 1).

This allows you to write more readable code using symbolic names while still working with simple values behind the scenes.

Fundamental Enum operations

Now that you've defined a basic Enum, you can perform fundamental operations like accessing, iterating, and comparing members to make your code more robust.

Accessing Enum members by name and value

from enum import Enum

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

print(Season(2))         # Access by value
print(Season['AUTUMN'])  # Access by name--OUTPUT--Season.SUMMER
Season.AUTUMN

You can retrieve specific enum members using either their value or their name. This flexibility is useful when you need to look up a member dynamically.

  • By value: Call the enum class with the value as an argument, like Season(2), which returns Season.SUMMER.
  • By name: Use square bracket notation with the member's name as a string, as in Season['AUTUMN'].

These lookups are handy when you're working with data—such as user input or database records—that corresponds to your enum definitions.

Iterating through Enum members

from enum import Enum

class Direction(Enum):
   NORTH = 'N'
   SOUTH = 'S'
   EAST = 'E'
   WEST = 'W'

for direction in Direction:
   print(direction.name, "->", direction.value)--OUTPUT--NORTH -> N
SOUTH -> S
EAST -> E
WEST -> W

You can loop over an enum to access all its members in the order they're defined. The Direction class itself is iterable, so a for loop works directly on it.

  • Each time the loop runs, you get the complete member object, like Direction.NORTH.
  • From there, you can easily pull its .name and .value attributes.

This pattern is great for validating input or dynamically generating UI elements from all available options.

Comparing and using Enum members

from enum import Enum

class Status(Enum):
   PENDING = 1
   ACTIVE = 2
   INACTIVE = 3

current_status = Status.ACTIVE
if current_status == Status.ACTIVE:
   print("The status is active")--OUTPUT--The status is active

You can compare enum members directly using equality operators like ==. This makes your conditional logic much clearer than using raw numbers or strings. The check current_status == Status.ACTIVE is self-explanatory and robust.

  • Improved Readability: Using named members like Status.ACTIVE makes your code's intent obvious without needing extra comments.
  • Type Safety: Comparisons are type-safe, so you can't accidentally compare a Status member with a member from a different enum, like Color.RED.

Advanced Enum techniques

Beyond the fundamentals, you can use powerful shortcuts like the auto() helper for value generation, create enums dynamically, and work with specialized types.

Using auto() to generate Enum values

from enum import Enum, auto

class Animal(Enum):
   DOG = auto()
   CAT = auto()
   BIRD = auto()
   FISH = auto()

print(list((animal.name, animal.value) for animal in Animal))--OUTPUT--[('DOG', 1), ('CAT', 2), ('BIRD', 3), ('FISH', 4)]

The auto() helper spares you from manually assigning values to enum members. When you use it, Python automatically provides a value—by default, an incrementing integer starting from 1. This is ideal when the specific values don't matter, as long as they're unique.

  • It keeps your code cleaner and reduces boilerplate.
  • It helps prevent errors from accidentally assigning duplicate values.

Creating functional Enum classes

from enum import Enum

class Operation(Enum):
   ADD = '+'
   SUBTRACT = '-'
   MULTIPLY = '*'
   DIVIDE = '/'
   
   def apply(self, x, y):
       if self == Operation.ADD: return x + y
       elif self == Operation.SUBTRACT: return x - y
       elif self == Operation.MULTIPLY: return x * y
       elif self == Operation.DIVIDE: return x / y

print(Operation.ADD.apply(5, 3))
print(Operation.MULTIPLY.apply(5, 3))--OUTPUT--8
15

You can add methods to your Enum classes to bundle behavior with your constants. In the Operation enum, the apply method performs a calculation based on which member it's called on. It uses self to check which operation to perform.

  • This turns your enum from a simple collection of constants into a more powerful, object-oriented tool.
  • Calling Operation.ADD.apply(5, 3) directly executes the addition, keeping the logic neatly contained within the enum itself.

Using specialized Enum types

from enum import IntEnum, Flag, auto

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

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

print(Priority.HIGH > Priority.LOW)
print(Permission.READ | Permission.WRITE)--OUTPUT--True
Permission.READ|Permission.WRITE

Python offers specialized enums for specific needs. IntEnum creates members that also behave like integers, so you can perform standard comparisons. For instance, Priority.HIGH > Priority.LOW works just as you'd expect because the members are also integers.

Flag is another useful type that supports bitwise operations, letting you combine members.

  • You can use the | operator to merge permissions, like in Permission.READ | Permission.WRITE.
  • It's perfect for managing settings or access rights where multiple options can be active at once.

Move faster with Replit

Replit is an AI-powered development platform that transforms natural language into working applications. Describe what you want to build, and Replit Agent creates it—complete with databases, APIs, and deployment.

For the enum techniques covered in this article, Replit Agent can turn them into production-ready tools. You could build:

  • A functional calculator that uses an Operation enum to manage its arithmetic logic.
  • A project management dashboard where task statuses like PENDING, ACTIVE, and INACTIVE are managed with a Status enum.
  • A user settings panel that uses a Permission flag enum to combine access rights such as READ, WRITE, and EXECUTE.

Simply describe your application idea, and Replit Agent will write the code, test it, and fix issues automatically, all within your browser.

Common errors and challenges

While enums are powerful, you might run into a few common issues, but they're easy to solve once you know what to look for.

Handling incorrect Enum value lookups

Trying to access an enum member with a value that doesn't exist, such as Season(5), will raise a ValueError. This can crash your program if you don't handle it.

  • To prevent this, wrap the lookup in a try-except block.
  • This lets you catch the ValueError and respond gracefully, perhaps by assigning a default value or logging a warning for debugging.

Avoiding duplicate Enum values

By default, Python allows multiple enum members to share the same value. When this happens, the second member becomes an alias for the first, which can cause confusion. For example, if ACTIVE = 1 and ENABLED = 1, then Status.ENABLED is just another name for Status.ACTIVE.

To enforce uniqueness, apply the @unique decorator from the enum module right above your class definition. If any values are duplicated, Python will raise a ValueError when the enum is created, catching the error early.

Debugging Enum comparison issues

A frequent mistake is comparing an enum member directly to its raw value, like current_status == 1. This comparison will almost always return False because you're comparing an enum object to an integer, not two identical values.

  • The main exception is when using IntEnum, where members behave like integers.
  • For standard enums, always compare members to other members for identity checks: current_status == Status.PENDING.
  • If you need to check the underlying value, use the .value attribute explicitly, as in current_status.value == 1.

Handling incorrect Enum value lookups

What happens when you try to look up an enum member using a value that doesn't exist? Your program will hit a roadblock, raising a ValueError that can cause a crash. The code below shows this exact scenario in action.

from enum import Enum

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

invalid_color = Color(4)

Since the Color enum has no member with the value 4, the lookup Color(4) fails and raises a ValueError. The code below shows how you can handle this lookup safely to prevent your program from crashing.

from enum import Enum

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

try:
   invalid_color = Color(4)
except ValueError:
   invalid_color = None
   print("No color with value 4 exists")

By placing the lookup inside a try block, you can safely handle potential ValueError exceptions. If the lookup fails, the except block is executed, preventing a crash. In this example, it sets the variable to None and prints a message. This defensive pattern is crucial when your code receives data from external sources, such as user input or API responses, which you can't always trust to be valid.

Avoiding duplicate Enum values

By default, enums permit duplicate values, where one member becomes an alias for another. This can create ambiguity, especially when you look up a member by its value. For example, what does Status(1) return? The following code shows this in action.

from enum import Enum

class Status(Enum):
   PENDING = 1
   WAITING = 1  # Duplicate value!
   ACTIVE = 2
   
print(Status(1))  # Which one will this return?

Since PENDING is defined first, the lookup Status(1) returns Status.PENDING, making WAITING an alias. This can hide bugs in your logic. The code below shows how to enforce unique values and prevent this issue.

from enum import Enum, unique

@unique
class Status(Enum):
   PENDING = 1
   WAITING = 2  # Changed to a unique value
   ACTIVE = 3
   
print(Status(1))  # Now this is unambiguous

To prevent aliases and ensure every member has a distinct value, apply the @unique decorator from the enum module directly above your class definition. This makes your enums more robust by catching errors early.

  • If Python finds any duplicate values, it will raise a ValueError when the enum is created.

This is crucial when you need unambiguous constants, as it prevents one member from accidentally masquerading as another during lookups.

Debugging Enum comparison issues

A common pitfall is comparing an enum member directly to its raw value, which can lead to silent bugs. Since an enum member is an object, not just its value, a check like size == 2 will fail. The code below shows this mistake.

from enum import Enum

class Size(Enum):
   SMALL = 1
   MEDIUM = 2
   LARGE = 3

size = Size.MEDIUM
if size == 2:  # Incorrect comparison
   print("Medium size selected")

The check size == 2 returns False because it compares the Size.MEDIUM object to a raw integer. As a result, the code inside the if block never runs. The example below shows how to fix this.

from enum import Enum

class Size(Enum):
   SMALL = 1
   MEDIUM = 2
   LARGE = 3

size = Size.MEDIUM
if size == Size.MEDIUM:  # Correct comparison
   print("Medium size selected")
# Or if you need to compare with the raw value:
if size.value == 2:
   print("Medium size selected")

The fix is to compare enum members with other members, not raw values. The check size == 2 fails because you're comparing a Size.MEDIUM object to an integer. This is a common source of silent bugs in conditional logic.

Always use one of these two correct approaches:

  • Compare members directly for readable, type-safe code: size == Size.MEDIUM.
  • If you need to check the raw value, use the .value attribute: size.value == 2.

Real-world applications

After mastering common challenges, you can use enums to manage application states or create dynamic menus, making your code cleaner and more intuitive.

Using Enum for state management in a task tracker

An enum is ideal for managing a task's lifecycle, as it provides a fixed set of statuses like TODO and DONE that make your code's logic clear and predictable.

from enum import Enum

class TaskStatus(Enum):
   TODO = 1
   IN_PROGRESS = 2
   REVIEW = 3
   DONE = 4

def update_task(task_name, new_status):
   print(f"Task '{task_name}' updated to: {new_status.name}")
   return new_status

current_status = update_task("Complete project report", TaskStatus.IN_PROGRESS)
current_status = update_task("Complete project report", TaskStatus.DONE)

The TaskStatus enum defines the only valid states a task can have. The update_task function accepts one of these members as its new_status argument, ensuring that you can only use a legitimate status from the enum.

  • Passing a member like TaskStatus.IN_PROGRESS makes the function call explicit and safe.
  • The function uses the .name attribute to print a readable string, such as 'IN_PROGRESS', which keeps the code's logic separate from its display output.

Creating a menu system with Enum and associated actions

By pairing enum members with callable actions, you can build a dynamic menu system where each option executes a specific function.

from enum import Enum
from typing import Callable

class MenuItem(Enum):
   SAVE = ('Save file', lambda: print("File saved successfully"))
   OPEN = ('Open file', lambda: print("File browser opened"))
   EXIT = ('Exit program', lambda: print("Exiting program..."))
   
   def __init__(self, label, action):
       self.label = label
       self.action = action
   
   def execute(self):
       return self.action()

# Simulate user selecting menu items
MenuItem.SAVE.execute()
MenuItem.EXIT.execute()

This example demonstrates how you can attach behavior directly to enum members. Each member, like SAVE, is assigned a tuple containing a string and a lambda function. The enum's custom __init__ method is automatically called for each member, assigning the tuple's contents to self.label and self.action.

  • The execute method provides a clean way to run the function associated with a member.
  • Calling MenuItem.SAVE.execute() invokes the specific lambda function stored in the SAVE member, effectively linking a constant to a specific action.

Get started with Replit

Turn your knowledge into a real tool. Describe your idea to Replit Agent, like “build a calculator using an enum for operations” or “create a task manager with enum-based statuses like ‘To Do’ and ‘Done’.”

The agent writes the code, tests for errors, and deploys your app automatically. Start building with Replit.

Get started free

Create and deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.

Get started for free

Create & deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.