Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use definition order of Enum as natural order

I'm trying to create an Enum subclass whose values use their definition order as their natural sort order, like in the example below:

@functools.total_ordering
class SelectionType(enum.Enum):
     character = 'character'
     word = 'word'
     sentence = 'sentence'
     paragraph = 'paragraph'

     def __le__(self, other):
          if not isinstance(other, SelectionType):
                return NotImplemented

          return self._positions[self] < self._positions[other]

SelectionType._positions = {x: i for i, x in enumerate(SelectionType)}

Is there a more direct way to get the position of an enum value in its definition order or otherwise a better way to do this?

like image 601
Feuermurmel Avatar asked Feb 21 '17 14:02

Feuermurmel


Video Answer


1 Answers

If this is a pattern you need often, or if the values are important and cannot be replaced by numbers, make a custom Enum you can inherit from:

import enum

class ByDefinitionOrderEnum(enum.Enum):

    def __init__(self, *args):
        try:
            # attempt to initialize other parents in the hierarchy
            super().__init__(*args)
        except TypeError:
            # ignore -- there are no other parents
            pass
        ordered = len(self.__class__.__members__) + 1
        self._order = ordered

    def __ge__(self, other):
        if self.__class__ is other.__class__:
            return self._order >= other._order
        return NotImplemented

    def __gt__(self, other):
        if self.__class__ is other.__class__:
            return self._order > other._order
        return NotImplemented

    def __le__(self, other):
        if self.__class__ is other.__class__:
            return self._order <= other._order
        return NotImplemented

    def __lt__(self, other):
        if self.__class__ is other.__class__:
            return self._order < other._order
        return NotImplemented

This allows you to keep any other value instead, while still sorting according to definition order.

class SelectionType(ByDefinitionOrderEnum):

     character = 'character'
     word = 'word'
     sentence = 'sentence'
     paragraph = 'paragraph'

and in use:

>>> SelectionType.word < SelectionType.sentence
True

>>> SelectionType.word.value < SelectionType.sentence.value
False
like image 119
Ethan Furman Avatar answered Sep 28 '22 08:09

Ethan Furman