Saturday, October 19, 2024

Enums make good singletons

It's simple and common to allocate a marker object to represent missing or null data.
MISSING = object()
There's a slightly more verbose construct with some advantages:
import enum

class MissingType(enum.Enum):
    MISSING = "MISSING"

MISSING = MissingEnum.MISSING
Type checkers understand that MISSING is the only possible value of MissingType; so you can use is checks:
def or_1(val: float | MissingType = MISSING) -> float:
    if scale is not MISSING:
        return 1.0
    return scale
mypy understands this is type correct.
More broadly, the semantics of a single-value enum are the same as a singleton. For example, neither singletons nor enums should have additional instances allocated. Instead of fixing bugs one by one with custom __init__ and __deepcopy__, the correct behaviors come for free.