使用Enum的定义顺序作为自然顺序

时间:2017-02-21 14:15:05

标签: python enums natural-sort

我试图创建一个Enum子类,其值使用其定义顺序作为其自然排序顺序,如下例所示:

@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)}

是否有更直接的方法来获取枚举值在其定义顺序中的位置,或者更好的方法来执行此操作?

2 个答案:

答案 0 :(得分:4)

您可以将位置编码为值。使用.name获取名称。

class SelectionType(enum.Enum):
     character = 1
     word = 2
     sentence = 3
     paragraph = 4
     # copy the OrderedEnum recipe from https://docs.python.org/3/library/enum.html#orderedenum
     def __lt__(self, other):
         if self.__class__ is other.__class__:
             return self.value < other.value 
         return NotImplemented

>>> SelectionType.word.name
'word'
>>> SelectionType.word < SelectionType.sentence
True

在Python 3.6+上,您可以使用enum.auto()来避免对位置进行硬编码。

class SelectionType(enum.Enum):
    character = enum.auto()
    word = enum.auto()
    sentence = enum.auto()
    paragraph = enum.auto()

答案 1 :(得分:4)

如果这是您经常需要的模式,或者如果值很重要且无法用数字替换,请创建一个可以继承的自定义枚举:

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

这允许您保留任何其他值,同时仍按照定义顺序进行排序。

class SelectionType(ByDefinitionOrderEnum):

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

并在使用中:

>>> SelectionType.word < SelectionType.sentence
True

>>> SelectionType.word.value < SelectionType.sentence.value
False