ShopperCove
Menu
All writingBlogTopicsCategoriesAboutRSS
Blog
Categories
Observability & SRE62All categories
About

Plate 10

  1. Blog

Enum vs Constants: Access Cost Lab

Aditya Challa·30 September 2026·4 min read

Summary
On this page
  1. Intro — what this post promises
  2. Arms
  3. Lab topology
  4. Lead table — read / compare (p50)
  5. Membership, construct, flags
  6. Creation (rare path)
  7. Reading it
  8. Pitfalls
  9. When to pick what
  10. Reproduce
  11. Closing

Intro — what this post promises

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:

  • itemgetter vs lambda sort localhost lab
  • ThreadPoolExecutor vs sequential localhost lab
  • set vs list membership localhost lab
  • dataclass vs slots vs dict localhost lab
  • lru_cache hit vs miss localhost lab
  • perf_counter vs time localhost lab
  • array vs list ints localhost lab
  • itertools vs python loops localhost lab

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.


Arms

ArmWhat
const int / strmodule-level STATUS_OK = 1 / "ok"
EnumStatus.OK.value / is / Status(v) / Status["OK"]
IntEnumint-compatible members
IntFlag`READWRITEEXEC`
membershipv in {1,2,3} vs values-set from Enum
create ×500define Enum class vs three assignments

Lab topology

N = 200000; repeats=9; p50 ops/s
Status(Enum), StatusInt(IntEnum), Flag(IntFlag)

Script: lab-evidence/61-enum-vs-constants/results/run_lab.py.


Lead table — read / compare (p50)

Armops/sns/op
const int read (×3 bundle)29,537,83033.9
IntEnum attr read16,273,59261.4
Enum .value read4,002,826249.8
const int ==24,457,38240.9
Enum member is17,528,09557.1
IntEnum == int16,485,51660.7
str const ==29,262,99734.2

Membership, construct, flags

Armops/sns/op
v in const set32,045,45031.2
v in Enum values set31,778,75731.5
Status(v) construct4,777,720209.3
StatusInt(v)4,741,658210.9
Status["OK"] name lookup18,847,03653.1
const bit OR30,536,23032.7
IntFlag OR941,0541062.6

Membership via a precomputed values set matched const sets (~1.01×). Calling Status(v) every time is the expensive validation path (~209 ns).


Creation (rare path)

Armops/sns/op
module consts ×50066,050,19015.1
Enum class ×50030,79932468.7

Metaclass work: consts ~2145× “faster” — irrelevant at import time once; relevant if you generate enums in a hot loop (don’t).


Reading it

  • IntEnum closes most of the gap vs plain ints on read (~1.82× vs Enum.value’s ~7.4×).
  • Compare members with is / identity when you already hold Enum objects — cheap; don’t call .value unless you need the int.
  • IntFlag is convenience, not a bit-twiddling speedup — ~32× behind bare int OR here.
  • API boundary: accept Enum in public functions; keep hot inner loops on ints if a profiler complains.

Pitfalls

  1. Banishing Enum after a microbench — clarity and invalid-value rejection matter more than 200 ns.
  2. Enum vs IntEnum mixups — plain Enum is not an int; Status.OK == 1 is False.
  3. Rebuilding membership sets every call — cache {m.value for m in Status}.
  4. Creating Enum classes dynamically in a loop — metaclass tax is huge.

When to pick what

NeedPrefer
Public status / APIEnum
Wire ints + typed namesIntEnum
Permission bitmasksIntFlag (clarity) or ints (speed)
Hottest numeric loopmodule ints

Reproduce

python3 lab-evidence/61-enum-vs-constants/results/run_lab.py

Evidence: /workspace/lab-evidence/61-enum-vs-constants/results/.


Closing

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.

enum.enumintenumintflagpython constantsattribute accesslocalhost labsreenum

Lab evidence

What I found running this

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

Notes when a lab post goes up

Occasional email for new hands-on reviews. No sequence and no sponsors.

Related links

  • Plate 15

    attrgetter vs getattr: Hot Loop Lab

    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.

    Observability & SRE · 30 Sept 2026

  • Plate 80

    dataclass vs slots vs dict: Python Object Lab

    Hands-on Python dataclass vs slots vs dict lab: instantiation and attribute ops/s plus RSS for classic, slots, dataclass, namedtuple, dict on localhost.

    Observability & SRE · 30 Sept 2026

  • Plate 17

    platform vs os.uname Inventory: Localhost Lab

    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.

    1 Oct 2026

On this page

  1. Intro — what this post promises
  2. Arms
  3. Lab topology
  4. Lead table — read / compare (p50)
  5. Membership, construct, flags
  6. Creation (rare path)
  7. Reading it
  8. Pitfalls
  9. When to pick what
  10. Reproduce
  11. Closing
All writingBlogCategoriesTopicsAboutPrivacyRSS

© 2026 ShopperCove