Enum Magic
Imagine you are writing a function that takes in levels of log levels and wants to do something such as printing or notifying specific individuals based on the level. For the sake of simplicity, the levels are info, warning, error:
def log(level: str, message: str) -> None:
...
If you use str as arguments, you are susceptible to
misspellings and potential unreal levels as someone might pass "errror" or
"critical". To resolve this, you should use Enum, but preferably use
StrEnum (Python 3.11+) with something like this:
from enum import StrEnum
class LogLevel(StrEnum):
INFO = "info"
WARNING = "warning"
ERROR = "error"
If, instead, you wanted to use integers to log different levels of
warnings you could use IntEnum. This is especially useful if
you wanted to do mathematical operations on these values, such as if you
wanted to sum the values of total ExitCode's generated.
from enum import IntEnum
class LogLevel(IntEnum):
INFO = 0
WARNING = 1
ERROR = 2
BIGERROR = 3
def log(level: LogLevel, message: str) -> None:
...
Or even better, you could use auto() for both
StrEnum
and IntEnum, this way you can guarantee uniqueness and adding
new values is easier. For StrEnum, it represents the value as
a lowercase of the real member name.
from enum import StrEnum, auto
class LogLevel(StrEnum):
INFO = auto() # "info"
WARNING = auto() # "warning"
ERROR = auto() # "error"
SUCCESS = auto() # "success"
For IntEnum, it automatically creates order and generates
uniqueness so no need to hardcode, and also makes adding new items easier.
from enum import IntEnum, auto
class LogLevel(IntEnum):
INFO = auto() # 1
WARNING = auto() # 2
ERROR = auto() # 3
SUCCESS = auto() # 4
Finally, you can also use Flags which are assigned bitwise
combinations of values (1, 2, 4, 8, 16). They are useful when you want to
represent multiple boolean options at once, and together. To combine these
use OR (|) and AND (&) operators.
from enum import Flag, auto
class Permission(Flag):
READ = auto() # 1
WRITE = auto() # 2
EXECUTE = auto() # 4
# Give read and write perms to the user
user_perms = Permission.READ | Permission.WRITE
Useful Decorators
A useful decorator would be the @unique, which guarantees
that you don't have duplicate values.
from enum import Enum, unique
@unique
class Status(Enum):
OK = 1
SUCCESS = 1 # ❌ ValueError: duplicate value 1
Another potentially useful decorator for ENUMs is @verify which
helps you confirm that your values satisfy a certain set of conditions.
from enum import Enum, verify, UNIQUE, CONTINUOUS
@verify(UNIQUE, CONTINUOUS)
class ErrorCode(Enum):
NOT_FOUND = 1
TIMEOUT = 2
UNKNOWN = 3 # ✅ values are unique + continuous (1, 2, 3)Questions or feedback? Feel free to reach out!