Notes to Self

Alex Sokolsky's Notes on Computers and Programming

String Enum in Python

Bare Python enum is not good enough: Int representation may be efficient, but it is not human readable, especially after it is serialized.

NOTE:

The color.py approach was sensible before Python 3.11, but should no longer be the preferred style:

Wouldn’t it be nice to have a string enum? Multiple inheritance to the resque!

'''
Demo of the better enum implementation
'''

from enum import Enum
from typing import Union


class Color(str, Enum):
    '''
    Possible colors
    see also https://fastapi-utils.davidmontague.xyz/user-guide/basics/enums/
    '''
    red = 'Red'
    green = 'Green'
    blue = 'Blue'
    black = 'Black'

    @classmethod
    def is_dark(cls, st: Union[str, 'Color']) -> bool:
        '''
        Dark color recognizer
        '''
        return st in [cls.black]

    @classmethod
    def is_primary(cls, st: Union[str, 'Color']) -> bool:
        '''
        Primary color recognizer
        '''
        return st in [cls.red, cls.green, cls.black]

    @classmethod
    def is_valid(cls, st: Union[str, 'Color']) -> bool:
        '''
        Valid color recognizer
        '''
        return st in Color._value2member_map_

    def __repr__(self):
        '''
        To enable Color serialization as a string...
        '''
        # default implementation
        #type_ = type(self)
        #module = type_.__module__
        #qualname = type_.__qualname__
        #return f"<{module}.{qualname} object at {hex(id(self))}>"
        return repr(self.value)

Let’s see how this works for Color user…

>>> r = Color.red
>>> r == 'Red'
True
>>> r == 'red'
False
>>> r
'Red'
>>> Color.is_primary( r )
True
>>> Color.is_primary( 'Red' )
True
>>> j = { 'color': r }
>>> j
{'color': 'Red'}
>>> print( j )
{'color': 'Red'}

I like it much more than bare enums!

Better auto yet…

This subject gets traction! Checkout this FastAPI write-up!

Modern sources

For ordinary application enums:

from enum import StrEnum


class Color(StrEnum):
    RED = 'Red'
    GREEN = 'Green'
    BLUE = 'Blue'
    BLACK = 'Black'

    @property
    def is_dark(self) -> bool:
        return self is Color.BLACK

Parse external input explicitly:

color = Color(raw_value)  # raises ValueError for unknown input

The PersistedEnum (line 30) style is stronger when values are written to evidence, databases, or files: