Enums
Enumerations replace magic strings and integers with named, typed constants. Members are singletons - identity-stable and self-documenting in APIs, configs, and match statements.
Search across all documentation pages
Enumerations replace magic strings and integers with named, typed constants. Members are singletons - identity-stable and self-documenting in APIs, configs, and match statements.
from enum import StrEnum, auto
class Status(StrEnum):
PENDING = "pending"
ACTIVE = "active"
CLOSED = "closed"
class Priority(StrEnum):
LOW = auto()
MEDIUM = auto()
HIGH = auto()When to reach for this:
match branchingFlagfrom enum import Flag, StrEnum, auto
class Status(StrEnum):
PENDING = "pending"
ACTIVE = "active"
CLOSED = "closed"
class Role(StrEnum):
ADMIN = "admin"
DEV = "dev"
VIEWER = "viewer"
class Permission(Flag):
READ = auto()
WRITE = auto()
EXECUTE = auto()
ADMIN_PERMS = Permission.READ | Permission.WRITE | Permission.EXECUTE
def authorize(role: Role, required: Role) -> bool:
hierarchy = {Role.VIEWER: 0, Role.DEV: 1, Role.ADMIN: 2}
return hierarchy[role] >= hierarchy[required]
def can_read(perms: Permission) -> bool:
return Permission.READ in perms
def describe_status(status: Status) -> str:
match status:
case Status.PENDING:
return "waiting"
case Status.ACTIVE:
return "running"
case Status.CLOSED:
return "done"
if __name__ == "__main__":
print(authorize(Role.ADMIN, Role.DEV))
print(can_read(ADMIN_PERMS))
print(describe_status(Status.ACTIVE))What this demonstrates:
StrEnum serializes to string value for JSON logsFlag combines with | and tests with inmatch on enum members for exhaustive handlingauto() generates values for non-string enumsStatus.ACTIVE is Status.ACTIVE always True.str and Enum - compares equal to its string value.int - use cautiously when you need int behavior.Enum('Color', ['RED', 'GREEN']) for dynamic enums.| Class | Member type |
|---|---|
Enum | Generic |
StrEnum | str |
IntEnum | int |
Flag | bitmask int |
# JSON serialization
status = Status.ACTIVE
payload = {"status": status} # StrEnum -> "active" in many encoders
# get by value
Status("active") # returns Status.ACTIVEStatus.ACTIVE == "active" False on plain Enum. Fix: StrEnum or compare .value.@enum.unique decorator.Enum with tuple values (name, extra).| Alternative | Use When | Don't Use When |
|---|---|---|
Literal["a","b"] | Type hints only | Need runtime validation |
| const module strings | Tiny scripts | API stability matters |
Pydantic Literal | HTTP validation | Internal domain enums |
| database lookup table | Dynamic user-defined values | Fixed system states |
Enum runtime objects with identity. Literal only for static typing of fixed strings.
Often serializes as string value - verify with your JSON encoder (stdlib json uses value for StrEnum).
list(Status) or Status.__members__.values().
@unique on Enum class raises if two names map to same value unintentionally.
| union, & intersection, ~ invert within Flag definition.
Exhaustive matching - mypy/pyright warn on missing cases with strict settings.
Subclassing Enum discouraged except special cases - compose instead.
Integers incrementing from one - override if stable values needed across versions.
TextChoices/IntegerChoices are Django enum wrappers - similar patterns.
Store .value in column; reconstruct with Enum(value) on read.
Stack versions: This page was written for Python 3.14.0 (stable 3.14, maintenance 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, and uv 0.6+.
Reviewed by Chris St. John·Last updated Jul 16, 2026