Как представить 'Enum' в Python?
Описание проблемы:
Я в основном разработчик на C#, но в данный момент работаю над проектом на Python.
Как можно представить эквивалент перечисления (Enum) в Python? Я хочу узнать, какой подход лучше всего подходит для реализации аналогичной функциональности перечислений, которые доступны в C#. Какие конструкции или библиотеки следует использовать?
5 ответ(ов)
Вот одна из реализаций:
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
.
Если вам нужны числовые значения, вот самый быстрый способ:
dog, cat, rabbit = range(3)
В Python 3.x вы также можете добавить звездочный плейсхолдер в конце, который поглотит все оставшиеся значения диапазона, если вам не важна лишняя память и вы не можете посчитать:
dog, cat, rabbit, horse, *_ = range(100)
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-класс для работы с перечислениями, получая как прямой, так и обратный доступ к элементам.
Я согласен. Давайте не будем настаивать на строгой типизации в 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]
Этот подход действительно позволяет удобно работать с перечислениями, и в то же время вам не придется беспокоиться о случайных ошибках при вводе значений. Если возникнут другие вопросы или потребуется дополнительная помощь, не стесняйтесь спрашивать!
Я предпочитаю определять перечисления (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'}
Преобразование байтов в строку в Python 3
Как вернуть ключи словаря в виде списка в Python?
UnicodeDecodeError: Кодек 'charmap' не может декодировать байт X в позиции Y: символ отображается как <неопределённый>
Относительные импорты в Python 3
"TypeError: требуется объект, похожий на bytes, а не 'str' при обработке содержимого файла в Python 3"