Back to Blog
Python

Python Enums: How to Use Enum and IntEnum

python enums: Learn how to use Python enums to define fixed sets of constants, with practical examples covering Enum, IntEnum, auto(), iteration, and comparison.

PythonEnumIntEnumType SafetyCode Maintainability
A diagram showing a Python enum class with named members and values, illustrating type safety and iteration.

When you need to represent a fixed set of named constants, Python enums provide a structured way to define and work with them. Unlike a plain class with class attributes, an Enum gives you iteration, comparison, and type safety out of the box. This article covers how to define enums, use auto-generated values, compare members, and choose between Enum, IntEnum, and StrEnum for your use case.

Why Use Enums in Python

When you have a set of related constants, such as status codes, configuration options, or error types, a plain class with class attributes works but leaves room for mistakes. For example, two constants with the same value are indistinguishable, and there is no built-in way to iterate over all options. Python enums solve these problems by creating a type with named members that are instances of the enum itself. Each member is a singleton, so identity comparison works reliably, and the enum class provides iteration, lookup, and a clean representation.

Defining an Enum with the Enum Class

The standard library's enum module provides the Enum base class. To define an enum, create a class that inherits from Enum and assign class attributes to member names. Each member is an instance of the enum class, and its value is the assigned constant.

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

Now Color.RED is an instance of Color, and its value is 1. You can access the member by name or by value. The name and value properties give you the member's identity and its assigned value. This structure makes it easy to validate input and to map between external data and your code.

Using auto() to Assign Values Automatically

When you do not care about the actual numeric values, use auto() to let Python assign them sequentially. This is useful when the value is only an internal identifier and does not need to match an external system.

from enum import Enum, auto class Status(Enum): PENDING = auto() ACTIVE = auto() DONE = auto()

The values are generated starting at 1 by default. If you need a different starting point, you can override the _generate_next_value_ method, but in most cases the default is fine. auto() keeps the definition concise and avoids accidental duplicates.

Iterating and Accessing Enum Members

One of the main advantages of enums is that you can iterate over all members. The __members__ mapping provides a dictionary of member names to members, and iteration over the class yields the members in definition order.

for status in Status: print(status.name, status.value)

You can also look up a member by name or value. The Enum class provides __call__ for value lookup, but in Python 3.11 and later you can use the value argument directly. For example, Status(1) returns Status.PENDING. If the value does not exist, a ValueError is raised. This is useful when parsing input from a database or API.

Comparing Enums: Identity and Equality

Enum members are singletons, so is comparison works reliably. Two references to the same member are the same object. Equality also works, but it is important to understand that two distinct enum classes with the same member names and values are not equal to each other. For example, Color.RED is not equal to Status.PENDING even if both have value 1. This is intentional: enums are distinct types, and equality is type-sensitive. When you need to compare a member to a value, you must compare against the member itself, not the raw value.

IntEnum and StrEnum: When to Use Them

The enum module also provides IntEnum and StrEnum (the latter available from Python 3.11). IntEnum members are also int instances, so they can be used anywhere an integer is expected, such as in arithmetic or when passing to functions that require an integer. This is convenient when your enum values must interoperate with existing integer-based code.

StrEnum members are strings, which is useful when you need to serialize to JSON or use the value as a key in a dictionary where string keys are expected. However, be aware that using IntEnum or StrEnum means your members lose some of the type safety that a pure Enum provides, because they can be compared directly to raw integers or strings. Choose the base class based on how the values will be used in your system.

Enums and Type Hints: Improving Code Clarity

Using enums in type hints makes your code self-documenting. Instead of accepting an arbitrary integer or string, a function can declare that it expects a specific enum type. This helps catch errors at development time and makes the code easier to read.

def process_status(status: Status) -> None: if status is Status.PENDING: # handle pending pass

Type checkers like mypy can verify that the correct enum type is passed, reducing the chance of passing an invalid constant.

Common Pitfalls and How to Avoid Them

A common mistake is using a plain class with class attributes and then trying to iterate over them or compare them reliably. Another pitfall is accidentally creating duplicate member names, which raises an error. Also, be careful when using IntEnum in a context where the raw integer value is used as a key: two different enums with the same integer value could collide if you mix them in a dictionary. Always use the enum member itself as the key when possible.

Runtime Cost and Memory Usage

Enum members are created once when the class is defined. Each member is a singleton, so memory usage is minimal. Accessing a member by name is a simple attribute lookup, and iteration is efficient because it uses the internal __members__ mapping. There is no significant runtime overhead compared to using plain class attributes. The main cost is the initial class definition, which is negligible in most applications.

Custom Methods and Behavior

Enums can have methods just like any other class. This allows you to attach behavior to each member, such as a human-readable description or a method that returns a related value. For example, you might define a description property or a method that returns the next status.

class Status(Enum): PENDING = 1 ACTIVE = 2 DONE = 3 def is_final(self) -> bool: return self is Status.DONE

This keeps the logic that depends on the enum value close to the enum definition, making it easier to maintain.

python enums: Practical Usage and Code Examples | RYUSLOG DEV