Update: 2017-03-01
In Python 3.6 (and Aenum 2.0
1) Flag
and IntFlag
classes have been added; part of that was a new auto()
helper that makes this trivially easy:
>>> class AutoName(Enum):
... def _generate_next_value_(name, start, count, last_values):
... return name
...
>>> class Ordinal(AutoName):
... NORTH = auto()
... SOUTH = auto()
... EAST = auto()
... WEST = auto()
...
>>> list(Ordinal)
[<Ordinal.NORTH: 'NORTH'>, <Ordinal.SOUTH: 'SOUTH'>, <Ordinal.EAST: 'EAST'>, <Ordinal.WEST: 'WEST'>]
Original answer
The difficulty with an AutoStr
class is that the name of the enum member is not passed into the code that creates it, so it is unavailable for use. Another wrinkle is that str
is immutable, so we can't change those types of enums after they have been created (by using a class decorator, for example).
The easiest thing to do is use the Functional API:
Animal = Enum('Animal', [(a, a) for a in ('horse', 'dog')], type=str)
which gives us:
>>> list(Animal)
[<Animal.horse: 'horse'>, <Animal.dog: 'dog'>]
>>> Animal.dog == 'dog'
True
The next easiest thing to do, assuming you want to make a base class for your future enumeration use, would be something like my DocEnem
:
class DocEnum(Enum):
"""
compares equal to all cased versions of its name
accepts a doctring for each member
"""
def __new__(cls, *args):
"""Ignores arguments (will be handled in __init__)"""
obj = object.__new__(cls)
obj._value_ = None
return obj
def __init__(self, doc=None):
# first, fix _value_
self._value_ = self._name_.lower()
self.__doc__ = doc
def __eq__(self, other):
if isinstance(other, basestring):
return self._value_ == other.lower()
elif not isinstance(other, self.__class__):
return NotImplemented
return self is other
def __hash__(self):
# keep DocEnum hashable
return hash(self._value_)
def __ne__(self, other):
return not self == other
and in use:
class SpecKind(DocEnum):
REQUIRED = "required value"
OPTION = "single value per name"
MULTI = "multiple values per name (list form)"
FLAG = "boolean value per name"
KEYWORD = 'unknown options'
Note that unlike the first option, DocEnum
members are not str
s.
If you want to do it the hard way: subclass EnumMeta
and fiddle with the new Enum
's class dictionary before the members are created:
from enum import EnumMeta, Enum, _EnumDict
class StrEnumMeta(EnumMeta):
def __new__(metacls, cls, bases, oldclassdict):
"""
Scan through `oldclassdict` and convert any value that is a plain tuple
into a `str` of the name instead
"""
newclassdict = _EnumDict()
for k, v in oldclassdict.items():
if v == ():
v = k
newclassdict[k] = v
return super().__new__(metacls, cls, bases, newclassdict)
class AutoStrEnum(str, Enum, metaclass=StrEnumMeta):
"base class for name=value str enums"
class Animal(AutoStrEnum):
horse = ()
dog = ()
whale = ()
print(Animal.horse)
print(Animal.horse == 'horse')
print(Animal.horse.name, Animal.horse.value)
Which gives us:
Animal.horse
True
horse horse
1 Disclosure: I am the author of the Python stdlib Enum
, the enum34
backport, and the Advanced Enumeration (aenum
) library.