4 min read
How expensive is enum.Enum compared with module-level ints/strings? This lab measures attribute / value reads, equality, membership, Enum(value) construction, IntFlag bitwise OR, and a rare class-creation path on Linux localhost.
Related links:
Lab honesty (1 Oct 2026 IST): Python 3.13.5. N=200,000 loop iters for hot arms. Read arms count one “bundle” of three status reads per op. Affiliates: 0. Enum still wins on typed APIs and exhaustiveness — this is cost, not a ban.
Verdict up front: module int consts ~30M/s bundles vs Enum.value ~4.0M (~7.4×); IntEnum ~16M (~1.82×). IntFlag OR ~32× behind bare ints. Prefer Enum for API clarity; use IntEnum when you need int-like speed + typing.
| Arm | What | ||
|---|---|---|---|
| const int / str | module-level STATUS_OK = 1 / "ok" | ||
Enum | Status.OK.value / is / Status(v) / Status["OK"] | ||
IntEnum | int-compatible members | ||
IntFlag | `READ | WRITE | EXEC` |
| membership | v in {1,2,3} vs values-set from Enum | ||
| create ×500 | define Enum class vs three assignments |
Script: lab-evidence/61-enum-vs-constants/results/run_lab.py.
| Arm | ops/s | ns/op |
|---|---|---|
| const int read (×3 bundle) | 29,537,830 | 33.9 |
| IntEnum attr read | 16,273,592 | 61.4 |
Enum .value read | 4,002,826 | 249.8 |
const int == | 24,457,382 | 40.9 |
Enum member is | 17,528,095 | 57.1 |
IntEnum == int | 16,485,516 | 60.7 |
str const == | 29,262,997 | 34.2 |
| Arm | ops/s | ns/op |
|---|---|---|
v in const set | 32,045,450 | 31.2 |
v in Enum values set | 31,778,757 | 31.5 |
Status(v) construct | 4,777,720 | 209.3 |
StatusInt(v) | 4,741,658 | 210.9 |
Status["OK"] name lookup | 18,847,036 | 53.1 |
| const bit OR | 30,536,230 | 32.7 |
| IntFlag OR | 941,054 | 1062.6 |
Membership via a precomputed values set matched const sets (~1.01×). Calling Status(v) every time is the expensive validation path (~209 ns).
| Arm | ops/s | ns/op |
|---|---|---|
| module consts ×500 | 66,050,190 | 15.1 |
| Enum class ×500 | 30,799 | 32468.7 |
Metaclass work: consts ~2145× “faster” — irrelevant at import time once; relevant if you generate enums in a hot loop (don’t).
IntEnum closes most of the gap vs plain ints on read (~1.82× vs Enum.value’s ~7.4×).is / identity when you already hold Enum objects — cheap; don’t call .value unless you need the int.Enum vs IntEnum mixups — plain Enum is not an int; Status.OK == 1 is False.{m.value for m in Status}.| Need | Prefer |
|---|---|
| Public status / API | Enum |
| Wire ints + typed names | IntEnum |
| Permission bitmasks | IntFlag (clarity) or ints (speed) |
| Hottest numeric loop | module ints |
Evidence: /workspace/lab-evidence/61-enum-vs-constants/results/.
Enums cost a little; IntEnum costs less. On this box const int reads were ~7.4× Enum.value and ~1.82× IntEnum; IntFlag OR trailed bare bits by ~32×. Use Enum at boundaries; profile before ripping types out of a hot path.
Lab evidence
Lab 1 Oct 2026 IST. Python 3.13.5; N=200000. const int read 29.5M vs Enum.value 4.0M (~7.4x); vs IntEnum ~1.82x; IntFlag OR vs int bits ~32x; Enum class create vs module consts ~2145x. Affiliates: 0. Evidence: lab-evidence/61-enum-vs-constants/.
Hands-on operator.attrgetter vs builtin getattr lab: real ops/s for extract and sort by attribute versus direct access, measured on Linux localhost only.
Lab1 Oct 20264 min read
Hands-on Python dataclass vs slots vs dict lab: instantiation and attribute ops/s plus RSS for classic, slots, dataclass, namedtuple, dict on localhost.
Lab1 Oct 20266 min read
Hands-on platform.platform vs os.uname host inventory lab: real ops/s plus cache notes, measured on Linux localhost today in this hands-on lab for SREs.
Summary1 Oct 20264 min read