The enum module gives you the base class for creating enumerated constants, plus related types such as IntEnum, StrEnum (3.11+), Flag, and IntFlag. That is separate from the built-in enumerate() used in for loops; for the loop helper, see Python enumerate().
This guide follows a practical order: what an enum is with a minimal Status example, then .name and .value, lookups, loops, comparisons, auto(), string and integer flavors, methods, multi-field members, uniqueness and aliases, then a short contrast with dicts and loose constants.
PEP 435 defines enumerations as symbolic names bound to unique constant values; members compare by identity and the class is iterable.
Tested on: Python 3.13.3; kernel 6.14.0-37-generic.
What is an enum in Python?
An enum groups a fixed set of named values inside a Python class. Instead of scattering strings like "pending" and "approved", you refer to Status.PENDING and Status.APPROVED, so editors and type checkers can follow the symbols and typos fail fast.
Here is the smallest useful shape: subclass Enum, list the members, then read the member object, its name, and its value.
from enum import Enum
class Status(Enum):
PENDING = "pending"
APPROVED = "approved"
REJECTED = "rejected"
print(Status.PENDING)
print(Status.PENDING.name)
print(Status.PENDING.value)You should see Status.PENDING, then PENDING, then pending on three lines.
Access enum name and value
Every member has name (the identifier string) and value (whatever you assigned—string, int, tuple, and so on). Use value when you persist to JSON or write to a database; use name when you want a stable symbolic token independent of the stored value.
from enum import Enum
class Role(Enum):
ADMIN = 1
USER = 2
r = Role.ADMIN
print(r.name, r.value, sep=" | ")You should see ADMIN | 1. The name stays the Python identifier; the value is what you store or compare to external systems.
Get enum member by name or value
Call the class with the value, or use subscript syntax with the member name as a string. The mapping from names to members also lives on Status.__members__ if you need programmatic lookup without going through getattr.
from enum import Enum
class Status(Enum):
PENDING = "pending"
APPROVED = "approved"
print(Status("pending"))
print(Status["APPROVED"])You should see Status.PENDING and Status.APPROVED. Invalid names or values raise KeyError or ValueError.
Loop through enum members
Iteration walks members in definition order (aliases are skipped by default in the iteration protocol).
from enum import Enum
class Size(Enum):
S = "small"
M = "medium"
L = "large"
for item in Size:
print(item.name, "->", item.value)You should see three lines, one per size, in the order S, M, L.
Compare enum members
Use is when you mean “this exact member.” == between members of the same enum class also works and compares by identity in practice, but is reads clearly for symbolic constants. The Enum HOWTO stresses identity semantics, and plain Enum does not support ordered comparisons such as < between members.
from enum import Enum
class Phase(Enum):
READY = 1
RUNNING = 2
a = Phase.READY
print(a is Phase.READY, a == Phase.READY)You should see True twice. Do not compare a plain Enum member to a bare integer or unrelated string—use .value when you need that bridge.
Use auto() with Python Enum
auto() picks a value for you (ints counting upward by default), which keeps large enumerations tidy.
from enum import Enum, auto
class Task(Enum):
QUEUED = auto()
RUNNING = auto()
DONE = auto()
print(list(Task))You should see three members with integer values 1, 2, and 3 in order (the first auto() starts at 1 for plain Enum in current CPython).
Python Enum with string values
Plain Enum with str values works on any supported Python version and is the usual choice when you only need stable labels and serialization.
from enum import Enum
class Status(Enum):
PENDING = "pending"
APPROVED = "approved"
REJECTED = "rejected"
print(Status.PENDING.value)You should see pending. You still compare members with is / ==; the string lives in .value.
Python StrEnum
StrEnum was added in Python 3.11. It behaves like Enum, but each member is also a str, so you can pass it to APIs that expect strings without always dereferencing .value.
from enum import StrEnum
class Status(StrEnum):
PENDING = "pending"
APPROVED = "approved"
print(Status.PENDING + " queue")You should see pending queue. On Python 3.10 or earlier, keep using plain Enum with string values; StrEnum exists only from 3.11 onward.
Python IntEnum
Prefer Enum for most new code. Use IntEnum only when members must behave like integers—for example replacing legacy numeric constants or talking to C APIs that expect ints. The documentation warns that IntEnum and IntFlag break some normal enum invariants because comparisons to integers succeed.
from enum import IntEnum
class Http(IntEnum):
OK = 200
NOT_FOUND = 404
print(Http.OK == 200)You should see True, which would be False for a plain Enum with value 200.
IntFlag combines integer behavior with bitwise operators for flag sets; reserve it for bitmask-style APIs and otherwise ignore it until you need that pattern.
Add methods to an Enum class
Define regular methods on the class body the same way you would on any other class; they receive the member as self. For typed field bundles without enum semantics, compare with Python dataclasses.
from enum import Enum
class Status(Enum):
PENDING = "pending"
APPROVED = "approved"
REJECTED = "rejected"
def is_final(self):
return self in {Status.APPROVED, Status.REJECTED}
print(Status.PENDING.is_final(), Status.APPROVED.is_final())You should see False then True.
Python Enum with multiple attributes
Give each member a tuple (or other structure) and unpack it in __init__ so every member carries extra fields.
from enum import Enum
class HttpStatus(Enum):
OK = (200, "Success")
NOT_FOUND = (404, "Not Found")
def __init__(self, code, message):
self.code = code
self.message = message
print(HttpStatus.OK.code)
print(HttpStatus.OK.message)You should see 200 and Success.
Unique enum values using @unique
@unique raises ValueError while the class is built if two names share one value (which would create an alias). It is a development aid, not a runtime guard for user input.
from enum import Enum, unique
try:
@unique
class Color(Enum):
RED = 1
CRIMSON = 1
except ValueError as exc:
print(type(exc).__name__, "raised")You should see ValueError raised (the message explains the alias).
Enum aliases and duplicate values
If you assign the same value twice, the second name becomes an alias for the first member—Color.CRIMSON is Color.RED after the class is created. Iteration yields each canonical member once unless you inspect __members__ manually.
from enum import Enum
class Color(Enum):
RED = 1
CRIMSON = 1
GREEN = 2
print(Color.CRIMSON is Color.RED)You should see True.
Enum vs dictionary vs constants
| Approach | Strength | Weakness |
|---|---|---|
Enum |
Typed members, iteration, identity checks | Values fixed at class definition |
| Dict of constants | Easy to build at runtime | Typos in string keys, no member objects |
| Loose module-level ints/strings | Zero ceremony | No grouping, easy to collide |
Reach for Enum when the set is stable and part of your domain model; use dicts or databases when the mapping is truly data-driven.
Common mistakes with Python Enum
- Using raw strings everywhere instead of an enum, then hunting typos at runtime.
- Comparing plain
Enummembers directly to unrelated ints or strings instead of comparing members to members or comparing.valueto primitives. - Defaulting to
IntEnumwhen a normalEnumwould keep comparisons safer and clearer. - Serializing the member object to JSON instead of
.value(orstr(member)forStrEnum). - Expecting duplicate values to create distinct members—they alias.
- Treating enums as open sets you can subclass casually; extending enumerations has strict rules—often a new enum or a shared base protocol is clearer.
- Putting mutable values such as lists on members; if the list mutates, every alias sees the change and you break the “constant” mental model.
- Modeling rapidly changing runtime data as enum members; enums are for fixed symbolic sets, not dynamic catalogs.
Python Enum quick reference table
| Need | Use |
|---|---|
| Import | from enum import Enum |
| Create enum | class Status(Enum): ... |
| Access member | Status.PENDING |
| Member name | Status.PENDING.name |
| Member value | Status.PENDING.value |
| Lookup by value | Status("pending") |
| Lookup by name | Status["PENDING"] |
| Loop members | for item in Status: |
| Auto values | auto() |
| String-like enum | StrEnum (3.11+) |
| Integer-like enum | IntEnum |
| Enforce unique values | @unique |
| Add methods | Define methods in the class body |
Summary
Python’s Enum is the standard way to name a closed set of constants with identity-aware members, iteration, and safe lookups by name or value. Prefer plain Enum (often with string values), add auto() for dense numeric tables, reach for StrEnum on 3.11+ when you need real string behavior, and use IntEnum only for integer interoperability. Layer methods and __init__ when members carry extra data, guard accidental duplicates with @unique, and serialize with .value at system boundaries.

