Skip to main content

GeekZilla.io

Generic selectors
Exact matches only
Search in title
Search in content
Post Type Selectors

Python Enum: Complete Guide with Examples

python enum

Python’s enum module provides a way to define sets of symbolic names bound to unique, constant values. Before enums were available, developers typically used plain integers or class-level string constants — approaches that offer no type safety, no meaningful repr(), and no protection against accidental value reassignment.

Since Python 3.4 introduced the enum module via PEP 435,
enumerations have become a standard pattern for representing a fixed set of
related values, such as days of the week, HTTP methods, status codes, or
permission levels.

This guide covers everything you need to know about Python
enums: how to create them, which type to choose, how auto() and @unique work,
and how to apply them in real-world scenarios including Django models and REST
APIs.

Key Takeaways

1. Python enums are defined by subclassing Enum (or a mixin
like IntEnum) — no special syntax is required.

2. Five main types exist: Enum, IntEnum, StrEnum (3.11+),
Flag, and IntFlag.

3. auto() assigns values automatically; @unique prevents
duplicate values.

4. Enum members are immutable, iterable, and support
membership testing with ‘in’.

5. Real-world use cases include Django model choices, API
status codes, and permission flags.

What Is a Python Enum?

An enumeration (enum) is a set of symbolic names, called
members, that are bound to unique, constant values. The Python documentation
describes enums as similar to global variables, but with a more useful repr(),
grouping, type-safety, and other features.

Enums are part of the standard library’s enum module and do
not require any third-party installation.

Why Use Enums Instead of Constants?

Consider a typical pre-enum pattern:

# Old pattern
— fragile

STATUS_PENDING
= 0

STATUS_ACTIVE
= 1

STATUS_INACTIVE
= 2

This approach has several drawbacks:


No type safety — any integer is valid, not just 0, 1, or 2.


No grouping — the constants are scattered in the module namespace.


Poor repr() — print(STATUS_ACTIVE) outputs 1, not something descriptive.


Reassignment risk — STATUS_ACTIVE = 99 is silently accepted.

The enum equivalent resolves all of these:

from enum
import Enum

class
Status(Enum):

    PENDING  =
0

    ACTIVE   =
1

    INACTIVE =
2

print(Status.ACTIVE)
# Status.ACTIVE

print(Status.ACTIVE.value)
# 1

print(Status.ACTIVE.name)
# ‘ACTIVE’

Note: The enum module was added to Python’s standard
library in Python 3.4 (PEP 435). It is not available in Python 2 without the
separately maintained enum34 backport.

Creating a Python Enum

The most common way to define an enum is to subclass Enum from
the enum module.

Class-Based Definition

from enum
import Enum

class
Color(Enum):

    RED   = 1

    GREEN = 2

    BLUE  = 3

Each name (RED, GREEN, BLUE) is an enum member. Each member
has two key attributes:


.name  — the member’s string name (‘RED’, ‘GREEN’, ‘BLUE’)


.value — the assigned value (1, 2, 3)

Functional (Constructor) API

Python’s enum module also supports a functional API, useful
for creating enums dynamically:

from enum
import Enum

Color =
Enum(‘Color’, [‘RED’, ‘GREEN’, ‘BLUE’])

# Values are
automatically assigned: RED=1, GREEN=2, BLUE=3

You may also pass a string of space-separated names:

Direction =
Enum(‘Direction’, ‘NORTH SOUTH EAST WEST’)

Tip: The functional API is convenient for dynamically
generated enums, but the class-based syntax is generally preferred for
readability and IDE support.

Accessing and Using Enum Members

By Name and By Value

# By attribute
access

print(Color.RED)
# Color.RED

# By name
(string lookup)

print(Color[‘RED’])
# Color.RED

# By value

print(Color(1))
# Color.RED

Iterating Over Members

for color in
Color:


print(color.name, color.value)

# RED 1

# GREEN 2

# BLUE 3

Membership Testing

print(Color.RED
in Color)   # True

Comparison

Enum members compare by identity, not by value. Two members
are equal only if they are the same member:

Color.RED ==
Color.RED    # True

Color.RED ==
Color.GREEN  # False

Color.RED ==
1            # False  (use IntEnum if you need this)

Note: Regular Enum members do not compare equal to their
raw values (e.g., Color.RED != 1). If you need integer comparison, use IntEnum
instead.

Using auto() for Automatic Values

The auto() helper assigns values automatically so you don’t
need to specify them manually. The default behaviour depends on the enum base
class.

from enum
import Enum, auto

class
Direction(Enum):

    NORTH =
auto()

    SOUTH =
auto()

    EAST  =
auto()

    WEST  =
auto()

print(list(Direction))

#
[<Direction.NORTH: 1>, <Direction.SOUTH: 2>, <Direction.EAST:
3>, <Direction.WEST: 4>]

auto() Behaviour by Enum Type

Enum Base

auto()
Default Value

Notes

Enum

1, 2, 3 …
(integers)

Increments from
1

StrEnum

Lowercase
member name

‘NORTH’ becomes
‘north’

Flag

Powers of 2
(1,2,4,8…)

Ready for
bitwise ops

Custom
_generate_next_value_

User-defined

Override to
return any value

Customising auto() with _generate_next_value_

You can override _generate_next_value_ to control what auto()
returns:

from enum
import Enum, auto

class
UpperEnum(Enum):


@staticmethod

    def
_generate_next_value_(name, start, count, last_values):

        return
name.upper()

class
Color(UpperEnum):

    red   =
auto()

    green =
auto()

print(Color.red.value)
# ‘RED’

Preventing Duplicate Values with @unique

By default, assigning the same value to two members creates an
alias — the second name points to the first member. If you want every member to
have a distinct value, use the @unique decorator:

from enum
import Enum, unique

@unique

class
Status(Enum):

    PENDING =
1

    ACTIVE  =
1   # <– raises ValueError: duplicate values found

Without @unique, the duplicate would silently become an alias:

class
Status(Enum):

    PENDING =
1

    WAITING =
1   # alias for PENDING

Status.WAITING
is Status.PENDING  # True

All Python Enum Types Compared

Python’s enum module provides several specialised base classes
beyond the default Enum. Choosing the right one typically depends on the type
of values involved and the operations you need to perform.

Type

Inherits
From

Values Are

Added In

Best For

Enum

object

Any type

Python 3.4

General-purpose
named constants

IntEnum

int

Integers

Python 3.4

Numeric
comparison, C interop, legacy APIs

StrEnum

str

Strings

Python 3.11

String
constants, JSON keys, HTTP status labels

Flag

Enum

Powers of 2

Python 3.6

Bitwise flags,
permission sets

IntFlag

int + Flag

Integers
(flags)

Python 3.6

Bitwise flags
with integer operations

IntEnum — Integer-Compatible Enumerations

IntEnum inherits from both int and Enum. Members behave like
integers in comparisons and arithmetic, making them suitable for numeric APIs,
C interoperability, and situations where you need enum members to interoperate
with plain integers.

from enum
import IntEnum

class
Priority(IntEnum):

    LOW    = 1

    MEDIUM = 2

    HIGH   = 3

print(Priority.HIGH
> Priority.LOW)  # True

print(Priority.HIGH
> 2)             # True  (int comparison works)

print(Priority.HIGH
+ 1)             # 4     (arithmetic works)

Note: Because IntEnum members compare equal to integers,
they lose some of the type-safety that plain Enum provides. Use IntEnum only
when integer interoperability is genuinely required.

StrEnum — String-Compatible Enumerations (Python 3.11+)

Introduced in Python 3.11, StrEnum inherits from both str and
Enum. Members are strings, and auto() automatically assigns the lowercase
version of the member name.

from enum
import StrEnum, auto  # Python 3.11+

class
HTTPMethod(StrEnum):

    GET    =
auto()

    POST   =
auto()

    PUT    =
auto()

    DELETE =
auto()

print(HTTPMethod.GET)
# HTTPMethod.GET

print(HTTPMethod.GET.value)
# ‘get’

print(HTTPMethod.GET
== ‘get’) # True  (str comparison works)

StrEnum is particularly useful for:


JSON serialisation where values must be plain strings.


HTTP status or method labels in REST APIs.


Django or database field choices.

StrEnum Before Python 3.11

If you are on Python 3.10 or earlier, you can approximate
StrEnum by inheriting from both str and Enum:

# Python 3.10
and earlier workaround

class
StrEnum(str, Enum):

    pass

class
Color(StrEnum):

    RED   =
‘red’

    GREEN =
‘green’

Flag and IntFlag — Combinable Bitwise Enumerations

Flag and IntFlag are designed for permission-style or
option-flag scenarios where multiple values can be combined using bitwise
operators (|, &, ^, ~). Values should generally be powers of 2 (1, 2, 4, 8,
…) to avoid overlapping bits. auto() handles this automatically for Flag.

Flag Example

from enum
import Flag, auto

class
Permission(Flag):

    READ    =
auto()   # 1

    WRITE   =
auto()   # 2

    EXECUTE =
auto()   # 4

# Combining
flags

user_perms =
Permission.READ | Permission.WRITE

print(user_perms)
# Permission.READ|WRITE

# Testing for
a flag

print(Permission.READ
in user_perms)   # True

print(Permission.EXECUTE
in user_perms) # False

IntFlag Example

IntFlag works like Flag but also inherits from int, allowing
members to be used in integer contexts:

from enum
import IntFlag, auto

class
FileMode(IntFlag):

    READ    =
4

    WRITE   =
2

    EXECUTE =
1

mode =
FileMode.READ | FileMode.WRITE

print(int(mode))
# 6

Adding Methods to Enum Classes

You can define methods on an Enum class. Every member will
have access to those methods, which is useful for adding behaviour directly to
the enum.

from enum
import Enum

class
Planet(Enum):

    MERCURY =
(3.303e+23, 2.4397e6)

    VENUS   =
(4.869e+24, 6.0518e6)

    EARTH   =
(5.976e+24, 6.37814e6)

    def
__init__(self, mass, radius):


self.mass   = mass


self.radius = radius

    @property

    def
surface_gravity(self):

        G =
6.67430e-11

        return
G * self.mass / (self.radius ** 2)

print(Planet.EARTH.surface_gravity)
# ~9.802

Tip: Defining __str__ or __repr__ inside an Enum lets you
control exactly how members are displayed, which is useful for logging and
debugging.

Real-World Use Cases

1. Django Model Choices

Django encourages enum-based choices for CharField and
IntegerField. StrEnum (or the str + Enum pattern) integrates cleanly:

from django.db
import models

from enum
import StrEnum, auto

class
OrderStatus(StrEnum):

    PENDING
= auto()

    CONFIRMED
= auto()

    SHIPPED
= auto()

    DELIVERED
= auto()

class
Order(models.Model):

    status =
models.CharField(


max_length=20,


choices=[(s, s.value) for s in OrderStatus],


default=OrderStatus.PENDING,

    )

2. REST API Status Codes

from enum
import IntEnum

class
HTTPStatus(IntEnum):


OK                  = 200


CREATED             = 201


BAD_REQUEST         = 400


UNAUTHORIZED        = 401

    NOT_FOUND
= 404


INTERNAL_SERVER_ERR = 500

def
handle_response(status_code: int):

    status =
HTTPStatus(status_code)

    if status
== HTTPStatus.OK:

        return
‘Success’

    elif
status == HTTPStatus.NOT_FOUND:

        return
‘Resource not found’

3. File Permission Flags

from enum
import IntFlag

class
Permission(IntFlag):

    NONE    =
0

    READ    =
4

    WRITE   =
2

    EXECUTE =
1

    ALL     =
READ | WRITE | EXECUTE

def
check_access(user_perm: Permission, required: Permission) -> bool:

    return
required in user_perm

admin =
Permission.ALL

print(check_access(admin,
Permission.WRITE))  # True

Common Mistakes and How to Avoid Them

Pitfall 1: Comparing Enum to Raw Value

# Incorrect

if status ==
1:   # Always False for plain Enum

    …

# Correct

if status ==
Status.ACTIVE:

    …

# OR use
IntEnum if integer comparison is genuinely needed

Pitfall 2: Accidentally Creating Aliases

class
Color(Enum):

    RED  = 1

    ROUGE =
1   # alias — not a separate member!

list(Color)  #
Only [<Color.RED: 1>]

# Use @unique
to prevent this

Pitfall 3: Forgetting auto() Starts at 1, Not 0

class
Status(Enum):

    PENDING =
auto()   # 1, not 0

    ACTIVE  =
auto()   # 2

Note: auto() starts at 1 by default for plain Enum. If your
code assumes 0-based indexing, assign values explicitly or override
_generate_next_value_.

Pitfall 4: Using Enum Members as Dictionary Keys with JSON

Enum members are not directly JSON-serialisable. When
serialising to JSON, use member.value:

import json

data =
{‘status’: Status.ACTIVE.value}  # Correct

json.dumps(data)
# Works

#
json.dumps({‘status’: Status.ACTIVE}) raises TypeError

Python Version Notes


Python 3.4  — enum module introduced (PEP 435). Enum, IntEnum
available.


Python 3.6  — Flag and IntFlag added.


Python 3.6  — auto() introduced.


Python 3.11 — StrEnum added. enum module significantly expanded.


Python 3.12 — Further refinements to Flag and boundary behaviour.

Quick Reference: Which Enum Type Should You Use?

Goal

Use This

Example

Named constants
(any type)

Enum

class Color(Enum): RED=1

Integer
comparison / math

IntEnum

class Priority(IntEnum): LOW=1

String values
(3.11+)

StrEnum

class Status(StrEnum): OK=auto()

Combinable
flags

Flag / IntFlag

class Perm(Flag): READ=auto()

Auto-name
values

auto()

NORTH = auto()

Prevent
duplicates

@unique
decorator

@unique class Dir(Enum): …

Frequently Asked Questions

Question

Answer

Are Enum values
mutable?

No. Enum
members are immutable by design. Attempting to reassign a member raises an
AttributeError.

Can two members
share a value?

Yes — by
default the second name becomes an alias for the first. Use @unique to
prevent aliases.

Can Enum
members have methods?

Yes. Define
methods inside the Enum class body; they are available on every member.

How do I
convert a string to an Enum?

Use the class
call: Color(‘red’) or Color[‘RED’]. The former looks up by value, the latter
by name.

Is Enum
available in Python 2?

The built-in
enum module requires Python 3.4+. A backport (enum34) existed for Python 2,
but it is no longer maintained.

How does Enum
compare to a dict or class constants?

Enum provides
type safety, a meaningful repr(), iteration, membership testing, and
protection against accidental reassignment — none of which plain dicts or
class constants offer by default.

Conclusion

Python enums provide a clean, safe, and expressive way to
represent fixed sets of related values. By choosing the right enum type — Enum,
IntEnum, StrEnum, Flag, or IntFlag — you can match the data type and operations
your code actually needs.

To summarise the key guidance:


Use Enum as your default starting point for named constants.


Use IntEnum when integer comparison or arithmetic is required.


Use StrEnum (Python 3.11+) when values must interoperate with strings.


Use Flag or IntFlag when values need to be combined with bitwise
operators.


Use auto() to avoid hardcoding values manually, and @unique to prevent
accidental aliases.

Enums generally lead to more readable, maintainable code than
bare integers or string constants, particularly as codebases grow and team
sizes increase.

Picture of Johnathan Dale
Johnathan Dale

John is a cheerful and adventurous boy, loves exploring nature and discovering new things. Whether climbing trees or building model rockets, his curiosity knows no bounds.

Newsletter

Register now to get latest updates on promotions & coupons.