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.
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()
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"