Как я могу проверить порядок элементов в Python Enum? - PullRequest
5 голосов
/ 13 октября 2019

У меня есть enum.Enum подкласс:

class MyEnum(Enum):
    A = "apple"
    C = "cherry"
    B = "banana"

, и я хочу иметь возможность использовать < и >, чтобы видеть, приходит ли данный член до или после другого в определениипорядок, поэтому я мог бы сделать что-то вроде этого:

>>> MyEnum.A < MyEnum.B
True
>>> MyEnum.B >= MyEnum.C
True
>>> MyEnum.C < MyEnum.A
False

в зависимости от того, где значения появляются в определениях перечисления, не сами значения перечисления. Я знаю, что Enums сохраняют порядок, но нет способа найти, что было первым. Как я могу сделать это в Python 3.7?

Ответы [ 6 ]

3 голосов
/ 13 октября 2019

Вам необходимо переопределить операторы сравнения и каким-то образом проверить порядок имен сравниваемых членов перечисления. Я обнаружил, что _member_names_ сохраняет порядок определенных членов:

from enum import Enum
import functools


@functools.total_ordering
class MyEnum(Enum):
    A = "apple"
    C = "cherry"
    B = "banana"

    def __eq__(self, other):
        if isinstance(other, MyEnum):
            return (
                self._member_names_.index(self.name) ==
                self._member_names_.index(other.name)
            )
        return NotImplemented

    def __gt__(self, other):
        if isinstance(other, MyEnum):
            return (
                self._member_names_.index(self.name) >
                self._member_names_.index(other.name)
            )
        return NotImplemented


print(MyEnum.A < MyEnum.B)  # True
print(MyEnum.B >= MyEnum.C)  # True
print(MyEnum.C < MyEnum.A)  # False
2 голосов
/ 13 октября 2019

Вместо этого можно использовать IntEnums и использовать более подходящие имена:

import enum

class Fruits(enum.IntEnum):
    Apple = 0
    Cherry = 1
    Banana = 2

print(Fruits.Apple < Fruits.Banana) 
print(Fruits.Banana >= Fruits.Cherry) 
print(Fruits.Cherry < Fruits.Apple)

Вывод:

True
True
False

IntEnums можно сравнить сдруг друга и обычные целые числа - так что Fruits.Apple < 1000 является действительным.

Вы можете получить имя или значение из него следующим образом:

>>> print(Fruits.Apple.name)
Apple

>>> print(Fruits.Apple.value)
0
1 голос
/ 13 октября 2019

Используя рецепт OrderedEnum в качестве начала, легко создать собственный упорядоченный базовый класс перечисления:

class OrderedEnum(Enum):

    def __init__(self, value, *args, **kwds):
        super().__init__(*args, **kwds)
        self.__order = len(self.__class__)

    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 MyEnum(OrderedEnum):
    A = "apple"
    C = "cherry"
    B = "banana"
0 голосов
/ 13 октября 2019

Другие ответы были хорошим способом сделать с ENUM, но я хочу поделиться своим ответом, который не использует ENUM, но я убедился, что он работает как enum

Python 3.6.8 |Anaconda custom (64-bit)| (default, Dec 29 2018, 19:04:46)
[GCC 4.2.1 Compatible Clang 4.0.1 (tags/RELEASE_401/final)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> PREFERENCE_LIST = {
...   'apple' : 10,
...   'cherry' : 5,
...   'banana' : 1,
... }
>>>
>>>
>>>
>>> class MyPref:
...   def __init__(self, selection):
...     if selection not in PREFERENCE_LIST.keys():
...         raise Exception("Accepted values are only " + str(PREFERENCE_LIST.keys()))
...     self.selection = selection
...
...   def __lt__(self, other):
...     return PREFERENCE_LIST[other.selection] < PREFERENCE_LIST[self.selection]
...   def __gt__(self, other):
...     return PREFERENCE_LIST[other.selection] > PREFERENCE_LIST[self.selection]
...   def __eq__(self, other):
...     return PREFERENCE_LIST[other.selection] == PREFERENCE_LIST[self.selection]
...   def __repr__(self):
...     return 'MyPref("{}")'.format(self.selection)
...
>>>
>>> p1 = MyPref('apple')
>>> p1
MyPref("apple")
>>>
>>> p2 = MyPref('cherry')
>>> p2
MyPref("cherry")
>>>
>>> p1 > p2
False
>>>
>>> p2 > p1
True
>>>
>>> p1 == p2
False
>>>

Объяснение

  1. Использование списка предпочтений для определения в качестве основы для построения класса предпочтений

  2. В классе предпочтений мы делаем две вещи,Во-первых, если пользователь выбирает и сохраняет их.

  3. Во-вторых, используя __lt__ - магическую функцию python, чтобы определить, как должно происходить сравнение между двумя объектами класса предпочтения.

0 голосов
/ 13 октября 2019

Предполагая, что __dict__ вашего класса Enum сохраняет семантику упорядочения словаря в Python 3.7, вы можете получить порядок членов следующим образом:

>>> class E(enum.Enum):
...    A = 'apple'
...    C = 'cherry'
...    B = 'banana'
... 
>>> 
>>> members = [v for v in E.__dict__.values() if isinstance(v, E)]
>>> members
[<E.A: 'apple'>, <E.C: 'cherry'>, <E.B: 'banana'>]

Сравнения могут быть сделаны на основе позиций всписок.

0 голосов
/ 13 октября 2019

вы можете переопределить эти операторы в вашем классе enum, для вас будет лучше прочитать о модели данных здесь

, если мне понадобится это сделать, я, вероятно, создамсписок упорядоченных элементов, и проверьте его, когда вызывается функция, или измените любой элемент на кортеж с индексом, и измените __get__ функцию

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...