11

Как представить 'Enum' в Python?

11

Описание проблемы:

Я в основном разработчик на C#, но в данный момент работаю над проектом на Python.

Как можно представить эквивалент перечисления (Enum) в Python? Я хочу узнать, какой подход лучше всего подходит для реализации аналогичной функциональности перечислений, которые доступны в C#. Какие конструкции или библиотеки следует использовать?

5 ответ(ов)

3

Вот одна из реализаций:

class Enum(set):
    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError

Вот как её можно использовать:

Animals = Enum(["DOG", "CAT", "HORSE"])

print(Animals.DOG)

В данном примере класс Enum наследует от set и переопределяет метод __getattr__. Это позволяет вам обращаться к элементам перечисления как к атрибутам класса. Если вы запрашиваете атрибут, который присутствует в наборе, он возвращает его имя. Если атрибут отсутствует, возбуждается исключение AttributeError.

Таким образом, вы можете использовать этот класс для создания перечислений, как показано с классом Animals, где вы можете получить доступ к значению DOG, используя Animals.DOG.

2

Если вам нужны числовые значения, вот самый быстрый способ:

dog, cat, rabbit = range(3)

В Python 3.x вы также можете добавить звездочный плейсхолдер в конце, который поглотит все оставшиеся значения диапазона, если вам не важна лишняя память и вы не можете посчитать:

dog, cat, rabbit, horse, *_ = range(100)
0

Enum-класс может быть реализован в виде однострочной конструкции.

class Enum(tuple): __getattr__ = tuple.index

Вот как его можно использовать (прямой и обратный поиск, ключи, значения, элементы и т.д.):

>>> State = Enum(['Unclaimed', 'Claimed'])  # Создаем перечисление
>>> State.Claimed  # Обращаемся к элементу по имени
1
>>> State[1]  # Обращаемся к элементу по индексу
'Claimed'
>>> State  # Выводим все значения перечисления
('Unclaimed', 'Claimed')
>>> range(len(State))  # Получаем диапазон индексов
[0, 1]
>>> [(k, State[k]) for k in range(len(State))]  # Получаем пары (индекс, значение)
[(0, 'Unclaimed'), (1, 'Claimed')]
>>> [(k, getattr(State, k)) for k in State]  # Получаем пары (значение, индекс)
[('Unclaimed', 0), ('Claimed', 1)]

Таким образом, мы можем легко использовать наш Enum-класс для работы с перечислениями, получая как прямой, так и обратный доступ к элементам.

0

Я согласен. Давайте не будем настаивать на строгой типизации в Python, но я хотел бы защитить себя от глупых ошибок. Что мы думаем об этом?

class Animal(object):
    values = ['Horse', 'Dog', 'Cat']

    class __metaclass__(type):
        def __getattr__(self, name):
            return self.values.index(name)

Это помогает избежать конфликта значений при определении моих перечислений.

>>> Animal.Cat
2

Кроме того, есть ещё одно полезное преимущество: очень быстрые обратные поиски:

def name_of(self, i):
    return self.values[i]

Этот подход действительно позволяет удобно работать с перечислениями, и в то же время вам не придется беспокоиться о случайных ошибках при вводе значений. Если возникнут другие вопросы или потребуется дополнительная помощь, не стесняйтесь спрашивать!

0

Я предпочитаю определять перечисления (enum) в Python следующим образом:

class Animal:
    class Dog: pass
    class Cat: pass

x = Animal.Dog

Такой подход более устойчив к ошибкам по сравнению с использованием целых чисел, так как вам не нужно беспокоиться о том, чтобы целые числа оставались уникальными (например, если вы присвоите Dog = 1 и Cat = 1, у вас возникнут проблемы).

Также это более надежно, чем использование строк, поскольку вы не рискуете столкнуться с опечатками (например, x == "catt" не выдаст ошибки, но x == Animal.Catt вызовет исключение во время выполнения).


ДОПОЛНЕНИЕ: Вы даже можете улучшить это решение, позволив классам Dog и Cat наследоваться от специального класса символов с необходимой метаклассой:

class SymbolClass(type):
    def __repr__(self): return self.__qualname__
    def __str__(self): return self.__name__

class Symbol(metaclass=SymbolClass): pass

class Animal:
    class Dog(Symbol): pass
    class Cat(Symbol): pass

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

>>> mydict = {Animal.Dog: 'Wan Wan', Animal.Cat: 'Nyaa'}
>>> mydict
{Animal.Dog: 'Wan Wan', Animal.Cat: 'Nyaa'}
Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь