As the saying goes, if you want something done... I created the following enum subclass (I didn't add any new members so that's allowed):
class DefaultNameEnum(Enum):
"""Support for "Other"/default-name values"""
@classmethod
def _missing_(cls, value):
possible_member = cls._value2member_map_.get(value, None)
if possible_member is None:
possible_member = cls._create_pseudo_member_(value)
return possible_member
@classmethod
def _create_pseudo_member_(cls, value):
"""
Create a default-name member.
"""
default_member = cls._value2member_map_.get(None, None)
if default_member is None:
raise ValueError("%r is not a valid %s" % (value, cls.__name__))
# construct a singleton enum pseudo-member
other_member = object.__new__(cls)
other_member._name_ = default_member._name_
other_member._value_ = value
# use setdefault in case another thread already created a composite
# with this value
other_member = cls._value2member_map_.setdefault(value, other_member)
return other_member
def __eq__(self, other):
"""Overrides the default implementation"""
if isinstance(other, DefaultNameEnum):
return self._name_ == other._name_
return False
def __ne__(self, other):
return not self == other
This is based on the Flag
enum subclass. Its usage is quite simple, actually - Just define as None
whichever name you wish to have as your default. It is best illustrated using an example - consider the class:
class ABC(DefaultNameEnum):
A = 1
B = 2
C = 3
Other = None
Than, the following console calls will give:
>>> print([repr(mem) for mem in ABC])
... ['<ABC.A: 1>', '<ABC.B: 2>', '<ABC.C: 3>', '<ABC.Other: None>']
>>> ABC(123)
... '<ABC.Other: 123>'
>>> ABC(1) == ABC(2)
... False
>>> ABC(123) == ABC.Other
... True
>>> ABC(123) == ABC(1374)
... True
If you wish to take this implementation and use it, note the following points:
The behavior in the last line might be wanted and might not - depending on your usage. If this is an unwanted usage, just change the __eq__
method to compare names when either self._value_
or other._value_
are None
.
If you use this class, for the sake of representability you might wish for the default value's __repr__
to output '<ABC.Other>'
rather than '<ABC.Other: None>'
in the case of None
value. This could easily be accomplished by overriding the __repr__
method.
If you don't define a default member, the class will raise an exception upon calling it upon an unknown value (just like any Enum
subclass).
I also wish to note that in the above implementation I would've preferred to use a sunder member such as _default_name_
or _default_value_member_
rather than assigning None
, but alas the enum
module does not permit defining a new sunder member for Enum
subclasses.