I am writing a class to represent sequential stages of an industrial process, composed of ADMISSION
, PROCESSING
, QC
and DELIVERY
stages.
Each stage has a unique, progressive sequence number, a mnemonic name and a field keeping track of the number of instances going through it:
@dataclass
class Stage:
seq_id: int
name: str
n_instances: int
Since stages are well-known and not supposed to change during execution, I decided to gather them in an Enum.
I need said enum to have the following requirements:
enum members need to:
-
be subclasses of
Stage
, in order to avoid accessing theirvalue
and making them easier to use (akin toIntEnum
orStrEnum
). In particular:-
be comparable by their
seq_id
(e.g.Stages.DELIVERY > Stages.PROCESSING
is true) -
be usable as dictionary keys
-
use
name
as their__str__
representation.
-
-
have immutable, sequential
seq_id
s from 0 to n based on their declaration order -
name
is specified at member declaration. Usingauto()
results in the lower-cased member name
I managed to address point 2 and 3 in my implementation (see below).
How can I implement point 1 (and its subpoints)?
My implementation
My idea is to use Stage
instances as the enum members, and use their seq_id
s as member values.
-
seq_id
is no longer in theStage
class as it is stored directly
in the enum member’s_value_
; I made this choice because I deem
seq_id
s not to have meaning outside the enum. -
__lt__
,__eq__
,__str__
and__hash__
have been implemented
inside the enum rather than theStage
class for the same reason. -
Hashing is based on the member’s
_value_
, which is supposedly
immutable (hashingStage
would’ve been tricky due to its
mutability)
@dataclass
class Stage:
#seq_id: int
name: str
n_instances: int = 0
#should be superflous due to autonumbering(see below)
@unique
@total_ordering
#req no. 1 (members are subclasses of stage)
class Stages(Stage, Enum):
#req no. 1.1 (ordering)
def __lt__(self, other):
if self.__class__ is other.__class__:
return self.value < other.value
return NotImplemented
def __eq__(self, other):
if self.__class__ is other.__class__:
return self.value == other.value
return NotImplemented
#req no. 1.2 (can be dict key)
def __hash__(self):
return hash(self.seq_id)
#req no. 1.3
def __str__(self):
return self.name
def __new__(cls, label):
#autonumbering enum pattern for req no. 2
value = len(cls.__members__) + 1
obj = Stage.__new__(cls, label)
obj._value_ = value
return obj
#req no. 3
@override
def _generate_next_value_(name, start, count, last_values):
return name.lower()
ADMISSION = auto()
PROCESSING = auto()
QC = auto()
DELIVERY = auto()
Unfortunately, this yields the following error:
TypeError: object.__new__() takes exactly one argument (the type to instantiate)
Can you help put me fix my implementation, or put me on the right track?
1
Python’s Enum doesn’t support subclassing directly. Instead, you can create Stage instances as enum members and assign a unique seq_id to each one.