2017-02-21 4 views
4

Я пытаюсь создать подкласс Enum, значение которого использует порядок их определений в их естественном порядке сортировки, как в примере ниже:Используйте порядок определения 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)} 

Есть более прямой способ получить позицию значения перечисления в порядке определения или иначе лучший способ сделать это?

+0

Осторожнее с псевдонимами – wim

ответ

3

Вы можете кодировать позиции как значения. Используйте имя .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() 
+0

Лично я сделал бы 'от перечисления импорта Enum, auto' избежать' enum.' повторения. Тем не менее стоит +1. :) –

+0

@EthanFurman Абсолютно. Я стараюсь избегать «от» импорта в кратких примерах, чтобы избежать путаницы. – Feuermurmel

+0

Почему это предлагается вместо простого * использования * ['OrderedEnum'] (https://docs.python.org/3/library/enum.html#orderedenum)? – wim

1

Если это шаблон вам нужно часто, или если значения являются важными и не могут быть заменены числами, сделать заказ ENUM вы можете наследовать от:

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 
Смежные вопросы