pythonenumsmeta

Ordering enum value in python


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.


Solution

  • 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()