|$ curl https://forge-ai.dev/api/markdown?path=docs/python/abc-enums
$cat docs/abc-&-enums.md
updated Today·18-24 min read·published
ABC & Enums
Introduction
abc defines abstract base classes; enum defines enumerations. Together they make illegal states harder to represent.
ABC & abstractmethod
abc_demo.py
Python
| 1 | from abc import ABC, abstractmethod |
| 2 | |
| 3 | class Repository(ABC): |
| 4 | @abstractmethod |
| 5 | def get(self, id: int) -> dict: ... |
| 6 | |
| 7 | @abstractmethod |
| 8 | def save(self, row: dict) -> None: ... |
| 9 | |
| 10 | def get_or_default(self, id: int, default: dict | None = None) -> dict | None: |
| 11 | try: |
| 12 | return self.get(id) |
| 13 | except KeyError: |
| 14 | return default |
| 15 | |
| 16 | class MemoryRepo(Repository): |
| 17 | def __init__(self) -> None: |
| 18 | self._data: dict[int, dict] = {} |
| 19 | def get(self, id: int) -> dict: |
| 20 | return self._data[id] |
| 21 | def save(self, row: dict) -> None: |
| 22 | self._data[int(row["id"])] = row |
| 23 | |
| 24 | # Repository() # TypeError: abstract |
| 25 | repo = MemoryRepo() |
| 26 | repo.save({"id": 1, "name": "Ada"}) |
| 27 | print(repo.get(1)) |
Abstract Properties
abs_prop.py
Python
| 1 | from abc import ABC, abstractmethod |
| 2 | |
| 3 | class Shape(ABC): |
| 4 | @property |
| 5 | @abstractmethod |
| 6 | def area(self) -> float: ... |
| 7 | |
| 8 | class Rect(Shape): |
| 9 | def __init__(self, w: float, h: float): |
| 10 | self.w, self.h = w, h |
| 11 | @property |
| 12 | def area(self) -> float: |
| 13 | return self.w * self.h |
| 14 | |
| 15 | print(Rect(3, 4).area) |
Enum Basics
enum_basics.py
Python
| 1 | from enum import Enum, auto |
| 2 | |
| 3 | class Color(Enum): |
| 4 | RED = auto() |
| 5 | GREEN = auto() |
| 6 | BLUE = auto() |
| 7 | |
| 8 | print(Color.RED, Color.RED.name, Color.RED.value) |
| 9 | print(Color["RED"], Color(1)) |
| 10 | for c in Color: |
| 11 | print(c) |
StrEnum & IntEnum
strenum.py
Python
| 1 | from enum import StrEnum, IntEnum, auto |
| 2 | |
| 3 | class Status(StrEnum): |
| 4 | OK = "ok" |
| 5 | ERROR = "error" |
| 6 | PENDING = "pending" |
| 7 | |
| 8 | assert Status.OK == "ok" # StrEnum mixes with str |
| 9 | print(f"status={Status.OK}") |
| 10 | |
| 11 | class ExitCode(IntEnum): |
| 12 | SUCCESS = 0 |
| 13 | USAGE = 2 |
| 14 | FAILURE = 1 |
| 15 | |
| 16 | raise SystemExit(ExitCode.USAGE) |
ℹ
info
Prefer StrEnum for JSON/API string unions; IntEnum for numeric codes.
Flag & IntFlag
flag.py
Python
| 1 | from enum import Flag, auto |
| 2 | |
| 3 | class Perm(Flag): |
| 4 | READ = auto() |
| 5 | WRITE = auto() |
| 6 | EXEC = auto() |
| 7 | |
| 8 | rw = Perm.READ | Perm.WRITE |
| 9 | print(Perm.READ in rw) |
| 10 | print(rw & Perm.WRITE) |
| 11 | print(list(rw)) |
unique & Aliases
unique.py
Python
| 1 | from enum import Enum, unique |
| 2 | |
| 3 | @unique |
| 4 | class Direction(Enum): |
| 5 | N = "n" |
| 6 | S = "s" |
| 7 | E = "e" |
| 8 | W = "w" |
| 9 | # NORTH = "n" # would fail @unique as alias |
| 10 | |
| 11 | class Color(Enum): |
| 12 | RED = 1 |
| 13 | CRIMSON = 1 # alias of RED |
| 14 | print(Color.CRIMSON is Color.RED) |
Functional API
functional_enum.py
Python
| 1 | from enum import Enum |
| 2 | |
| 3 | Animal = Enum("Animal", ["ANT", "BEE", "CAT"]) |
| 4 | print(Animal.ANT, list(Animal)) |
collections.abc
For typing and isinstance checks against collection protocols, use collections.abc — Sequence, Mapping, Iterable, Callable, etc.
coll_abc.py
Python
| 1 | from collections.abc import Sequence, Mapping |
| 2 | |
| 3 | def first(xs: Sequence[int]) -> int: |
| 4 | return xs[0] |
| 5 | |
| 6 | def keys(m: Mapping[str, int]) -> list[str]: |
| 7 | return list(m) |
Patterns
| Goal | Tool |
|---|---|
| Force subclass implementation | ABC + abstractmethod |
| Closed set of string values | StrEnum |
| Bitset options | Flag |
| JSON-friendly status | StrEnum |
| Plugin interface | ABC |
ABC Meta register
register.py
Python
| 1 | from abc import ABC |
| 2 | from collections.abc import Sequence |
| 3 | |
| 4 | class MySeq(ABC): |
| 5 | pass |
| 6 | |
| 7 | MySeq.register(tuple) |
| 8 | assert issubclass(tuple, MySeq) |
| 9 | assert isinstance((1, 2), MySeq) |
| 10 | |
| 11 | # Virtual subclass — no inheritance required |
| 12 | # Useful for adapting third-party types to your ABC checks |
Methods on Enums
enum_methods.py
Python
| 1 | from enum import StrEnum |
| 2 | |
| 3 | class Priority(StrEnum): |
| 4 | LOW = "low" |
| 5 | MEDIUM = "medium" |
| 6 | HIGH = "high" |
| 7 | |
| 8 | def weight(self) -> int: |
| 9 | return {Priority.LOW: 1, Priority.MEDIUM: 2, Priority.HIGH: 3}[self] |
| 10 | |
| 11 | print(Priority.HIGH.weight()) |
| 12 | print(sorted(Priority, key=lambda p: p.weight())) |
Enums with match
match_enum.py
Python
| 1 | from enum import StrEnum |
| 2 | |
| 3 | class Op(StrEnum): |
| 4 | ADD = "add" |
| 5 | MUL = "mul" |
| 6 | |
| 7 | def apply(op: Op, a: int, b: int) -> int: |
| 8 | match op: |
| 9 | case Op.ADD: |
| 10 | return a + b |
| 11 | case Op.MUL: |
| 12 | return a * b |
| 13 | |
| 14 | print(apply(Op.ADD, 2, 3)) |
Pitfalls
- Forgetting to implement all abstract methods
- Using Enum mutable values (lists) — avoid
- Comparing Enum with bare strings without StrEnum
- Overusing Flag when a set[str] would be clearer
IntFlag
intflag.py
Python
| 1 | from enum import IntFlag, auto |
| 2 | |
| 3 | class Style(IntFlag): |
| 4 | BOLD = auto() |
| 5 | ITALIC = auto() |
| 6 | UNDERLINE = auto() |
| 7 | |
| 8 | s = Style.BOLD | Style.ITALIC |
| 9 | print(int(s), Style.BOLD in s) |
| 10 | print(s & ~Style.ITALIC) |
Typing Notes
Annotate with the Enum type itself: status: Status. For StrEnum, APIs can accept str and validate into the enum via constructors or Pydantic.
typing_enum.py
Python
| 1 | from enum import StrEnum |
| 2 | |
| 3 | class Status(StrEnum): |
| 4 | OK = "ok" |
| 5 | ERR = "error" |
| 6 | |
| 7 | def handle(status: Status) -> str: |
| 8 | return f"handled {status}" |
| 9 | |
| 10 | print(handle(Status("ok"))) |
Summary
Use ABCs to define implementable contracts; use Enum/StrEnum/Flag for closed value sets. Prefer StrEnum at stringly-typed boundaries and keep enum values immutable.
Checklist
- All abstract methods implemented before instantiation
- StrEnum for JSON string unions
- @unique when aliases are bugs
- Keep enum members immutable constants
- Prefer ABC over NotImplementedError stubs for public APIs
$Blueprint — Engineering Documentation·Section ID: PYTHON-ABC-ENUM·Revision: 1.0
Community
Get help on Slack, Discord or VIP
Stuck on a guide? Join the community and ask.