Python Enum

Learn Python Enum with simple examples, including enum class syntax, name and value, iteration, auto(), IntEnum, StrEnum, enum methods, multiple attributes, and common mistakes.

Published

Updated

Read time 7 min read

Reviewed byDeepak Prasad

Python Enum

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.

python
from enum import Enum


class Status(Enum):
    PENDING = "pending"
    APPROVED = "approved"
    REJECTED = "rejected"


print(Status.PENDING)
print(Status.PENDING.name)
print(Status.PENDING.value)
Output

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.

python
from enum import Enum


class Role(Enum):
    ADMIN = 1
    USER = 2


r = Role.ADMIN
print(r.name, r.value, sep=" | ")
Output

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.

python
from enum import Enum


class Status(Enum):
    PENDING = "pending"
    APPROVED = "approved"


print(Status("pending"))
print(Status["APPROVED"])
Output

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).

python
from enum import Enum


class Size(Enum):
    S = "small"
    M = "medium"
    L = "large"


for item in Size:
    print(item.name, "->", item.value)
Output

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.

python
from enum import Enum


class Phase(Enum):
    READY = 1
    RUNNING = 2


a = Phase.READY
print(a is Phase.READY, a == Phase.READY)
Output

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.

python
from enum import Enum, auto


class Task(Enum):
    QUEUED = auto()
    RUNNING = auto()
    DONE = auto()


print(list(Task))
Output

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.

python
from enum import Enum


class Status(Enum):
    PENDING = "pending"
    APPROVED = "approved"
    REJECTED = "rejected"


print(Status.PENDING.value)
Output

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.

python
from enum import StrEnum


class Status(StrEnum):
    PENDING = "pending"
    APPROVED = "approved"


print(Status.PENDING + " queue")
Output

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.

python
from enum import IntEnum


class Http(IntEnum):
    OK = 200
    NOT_FOUND = 404


print(Http.OK == 200)
Output

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.

python
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())
Output

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.

python
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)
Output

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.

python
from enum import Enum, unique


try:

    @unique
    class Color(Enum):
        RED = 1
        CRIMSON = 1

except ValueError as exc:
    print(type(exc).__name__, "raised")
Output

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.

python
from enum import Enum


class Color(Enum):
    RED = 1
    CRIMSON = 1
    GREEN = 2


print(Color.CRIMSON is Color.RED)
Output

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 Enum members directly to unrelated ints or strings instead of comparing members to members or comparing .value to primitives.
  • Defaulting to IntEnum when a normal Enum would keep comparisons safer and clearer.
  • Serializing the member object to JSON instead of .value (or str(member) for StrEnum).
  • 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.


References


Frequently Asked Questions

1. What is the difference between Enum and IntEnum?

Enum members are compared by identity and are not integers; IntEnum members subclass int so they compare to integers and are useful when you must interoperate with numeric APIs—prefer plain Enum unless you need that behavior.

2. When should I use StrEnum instead of Enum with string values?

Enum with str values is enough for labels and serialization; StrEnum (Python 3.11+) makes members actual str subclasses so they work in string APIs without always writing .value.

3. Can two enum members share the same value?

Yes—the second name becomes an alias for the first member; iteration skips aliases unless you use iter patterns that expose them—use @unique during development to forbid accidental duplicates.

4. Should I compare enum members with is or ==?

Both work between members of the same enum class; is expresses identity clearly, which matches how the Enum HOWTO describes member identity—avoid comparing plain Enum members to raw strings or ints.

5. How do I serialize an enum to JSON?

JSON has no enum type—emit the primitive you need, usually member.value (or str(member) for StrEnum), and rebuild with cls(value) or cls[name] on load.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive …