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.

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 returnsSeason.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
.nameand.valueattributes.
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.ACTIVEmakes your code's intent obvious without needing extra comments. - Type Safety: Comparisons are type-safe, so you can't accidentally compare a
Statusmember with a member from a different enum, likeColor.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 inPermission.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
Operationenum to manage its arithmetic logic. - A project management dashboard where task statuses like
PENDING,ACTIVE, andINACTIVEare managed with aStatusenum. - A user settings panel that uses a
Permissionflag enum to combine access rights such asREAD,WRITE, andEXECUTE.
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-exceptblock. - This lets you catch the
ValueErrorand 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
.valueattribute explicitly, as incurrent_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
ValueErrorwhen 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
.valueattribute: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_PROGRESSmakes the function call explicit and safe. - The function uses the
.nameattribute 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
executemethod provides a clean way to run the function associated with a member. - Calling
MenuItem.SAVE.execute()invokes the specific lambda function stored in theSAVEmember, 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.
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.
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.


.png)
.png)