Ordering enum value in python
Solution 1
Your Enum loses the ordering in 3 places. First the attributes on the class body are stored in a dictionary, then you copy the items into another dictionary. Finally your values()
returns a 3rd dictionary. A dictionary does not save ordering, and it is impossible to get the ordering of the attributes within the class body.
With this system the easiest is to have a variable
__ordering__ = [ 'single', 'double' ]
And make the values()
return a list of tuples (like dict.items()
).
class EnumMeta(type):
def __new__(typ, name, bases, attrs):
cls_attrs = {}
cls_choices = {}
for attr_name, value in attrs.items():
cls_attrs[attr_name] = attr_name.lower()
if not attr_name.startswith("__"):
cls_choices[attr_name.lower()] = value
ordering = attrs.get('__ordering__')
if ordering == None:
ordering = sorted(cls_choices.keys())
def choices(cls):
return dict(cls_choices)
def values(cls, value=None):
if value is None:
return [ (k, cls_choices[k] ) for k in ordering ]
elif not isinstance(value, basestring):
return [ (k, cls_choices[k] ) for k in value ]
else:
return unicode(cls_choices.get(value))
def keys(cls, nil=False):
items = list(ordering)
if nil:
items.append('')
return items
def combined_length(cls):
return len(",".join(cls.values().keys()))
def max_length(cls):
return max(map(len, cls.values().keys()))
cls_attrs['choices'] = classmethod(choices)
cls_attrs['values'] = classmethod(values)
cls_attrs['keys'] = classmethod(keys)
cls_attrs['combined_length'] = classmethod(combined_length)
cls_attrs['max_length'] = classmethod(max_length)
return type(name, bases, cls_attrs)
class SideHemType:
__ordering__ = ['double', 'single']
__metaclass__ = EnumMeta
Single = "Single side hem for opaque fabrics"
Double = "Double side hem for transparent fabrics"
print SideHemType.keys()
print SideHemType.values()
Solution 2
If you are using Python3.4 you can use the new enum.Enum
type, which remembers the order the enum members are declared in.
If you are using an earlier Python, you should use the enum34
package available from PyPI, which supports Pythons back to 2.4.
The enum34
package, if used in Python3, also remembers the order of member declarations. If used in Python 2 it supports an extra _order_
attribute:
from enum import Enum
class SideHemType(Enum):
_order_ = 'Single Double' # only needed in Python 2
Single = "Single side hem for opaque fabrics"
Double = "Double side hem for transparent fabrics"
@classmethod
def combined_length(cls):
return len(",".join(mbr.name for mbr in cls))
@classmethod
def max_length(cls):
return max(map(len, (mbr.name for mbr in cls)))
print list(SideHemType) # [SideHemType.Single, SideHemType.Double]
print SideHemType.Double.value # "Double side hem for transparent fabrics"
Solution 3
Use IntEnum
from the enum
package and use the integer values to specify the order that you want:
class Shape(IntEnum):
CIRCLE = 1
SQUARE = 2
Shape.CIRCLE < Shape.SQUARE
Prints True
.
Related videos on Youtube
Robert
Updated on September 14, 2022Comments
-
Robert about 1 year
I would like to be able to arrange the ordering of Enum. Has somebody suggestions how this can be solved?
The following Enum meta class is using:
class EnumMeta(type): def __new__(typ, name, bases, attrs): cls_attrs = {} cls_choices = [] for attr_name, value in attrs.items(): cls_attrs[attr_name] = attr_name.lower() if not attr_name.startswith("__"): cls_choices.append((attr_name.lower(), value)) def choices(cls): return cls_choices def values(cls, value=None): if value is None: return {choice[0]: unicode(choice[1]) for choice in cls.choices()} elif isinstance(value, list): return {choice[0]: unicode(choice[1]) for choice in cls.choices() if choice[0] in value} else: return unicode(dict(cls.choices()).get(value)) def keys(cls, nil=False): items = [item[0] for item in cls.choices()] if nil: items.append('') return items def combined_length(cls): return len(",".join(cls.values().keys())) def max_length(cls): return max(map(len, cls.values().keys())) cls_attrs['choices'] = classmethod(choices) cls_attrs['values'] = classmethod(values) cls_attrs['keys'] = classmethod(keys) cls_attrs['combined_length'] = classmethod(combined_length) cls_attrs['max_length'] = classmethod(max_length) return type(name, bases, cls_attrs)
An example of an Enum is as follow:
class SideHemType: __ordering__ = ['double', 'single'] __metaclass__ = EnumMeta Single = "Single side hem for opaque fabrics" Double = "Double side hem for transparent fabrics" class TestEnumOrdering: print SideHemType.keys() print SideHemType.values()
By printing the Enum SideHemType first Double is printed and then Single. But I would like first Single and then Double.
-
Robert about 10 yearsIt has no effect to add ordering the Enum, see my example.
-
jbch over 5 years@Robert __ordering__ is not magic, Antti also modified the code for EnumMeta, if you just add __ordering__ without using his EnumMeta code it will do nothing.
-
Michael almost 4 yearsWorth noting: even with Python 3.4+, if you have your own list of Enum members that you want to sort, you have to specify a
key
(sorted([SideHemType.Double, SideHemType.Single], key=lambda sht: sht.value)
). The Enum members do not implement__lt__
. You get a nice littleTypeError: '<' not supported between instances of
your Enum type. -
mac over 3 years@Michael-Where'sClayShirky....but then the sort order is based on the value, not the order declared, right?
-
ChrisCantrell about 3 yearsIf you are using the Python 3.4+ Enum, then you can define your own
__lt__
operator inside the Enum definition to compare name, value, or anything else you want. -
Thorben Croisé almost 3 yearsSimple and elegant, be aware that this became available in Python 3.4.