I was very happy to learn Enum
types can effectively carry named attributes:
from enum import Enum
class ActivityType(Enum):
NEXT = 0, "N"
JOIN = 3, "J"
DIVIDE_REAR = 1, "DR"
DIVIDE_FRONT = 2, "DF"
def __init__(self, xml_code, label):
self.xml_code = xml_code
self.label = label
assert ActivityType.JOIN.label == "J"
assert ActivityType.DIVIDE_FRONT.xml_code == 2
How can I do something similar for Flag
enums? Here, something can have a combination of PowerType
s, and the string representation has a code for each one which are combined in a specific order:
class PowerType(Flag):
NONE = 0
AC_OVERHEAD = auto()
DC_3RAIL = auto()
DIESEL = auto()
def xml_code(self):
powertype_subcode = {
PowerType.AC_OVERHEAD: "O",
PowerType.DC_3RAIL: "3",
PowerType.DIESEL: "D",
}
return "".join(sc for pt, sc in powertype_subcode.items() if pt & self)
assert (PowerType.DIESEL | PowerType.DC_3RAIL).xml_code() == "3D"
assert (PowerType.AC_OVERHEAD | PowerType.DC_3RAIL).xml_code() == "O3"
I'd like to do something like this and eliminate that powertype_subcode
dict:
class PowerType(Flag):
NONE = 0, ""
AC_OVERHEAD = auto(), "O"
DC_3RAIL = auto(), "3"
DIESEL = auto(), "D"
def __init__(self, flag, xml_code):
self.flag = flag
self._xml_subcode = xml_code
def xml_code(self):
return "".join(pt._xml_subcode for pt in PowerType if pt & self)
However this causes problems with auto()
and _generate_next_value_
and if I work around that, it then finds it can't do bitwise operations on tuple values.
Using @JackDeeth's answer as a basis, changing xml_code
to a property, and saving _xml_subcode
when absent, solves the problem:
from enum import Flag, auto
class PowerType(Flag):
NONE = 0
AC_OVERHEAD = auto(), "O"
DC_3RAIL = auto(), "3"
DIESEL = auto(), "D"
#
def __new__(cls, flag, xml_code = ""):
obj = object.__new__(cls)
obj._value_ = flag
obj._xml_subcode = xml_code
return obj
#
@property
def xml_code(self):
code = getattr(self, '_xml_subcode', None)
if code is None:
code = "".join(pt._xml_subcode for pt in self)
self._xml_subcode = code
return code
# Assertions still pass (after removing parenthesis from xml_code)
assert (PowerType.DIESEL | PowerType.DC_3RAIL).xml_code == "3D"
assert (PowerType.AC_OVERHEAD | PowerType.DC_3RAIL).xml_code == "O3"
assert (PowerType.DIESEL | PowerType.DC_3RAIL)._xml_subcode == "3D"
assert (PowerType.AC_OVERHEAD | PowerType.DC_3RAIL)._xml_subcode == "O3"
Note also that flags are themselves iterable, so instead of for pt in PowerType
I'm using for pt in self
.
Disclosure: I am the author of the Python stdlib Enum
, the enum34
backport, and the Advanced Enumeration (aenum
) library.