Как сравнить строку с перечислением python?

Я только что обнаружил существование Enum базового класса в python, и я пытаюсь представить, как это может быть полезно мне.

Скажем, я определяю статус светофора:

from enum import Enum, auto

class Signal(Enum):
    red = auto()
    green = auto()
    orange = auto()

Скажем, я получаю информацию от некоторой подсистемы в своей программе в виде строки, представляющей название цвета, например brain_detected_colour = "red".

Как сравнить эту строку с сигналами светофора?

Очевидно, brain_detected_colour is Signal.red является False, потому что Signal.red не является строкой.

Signal(brain_detected_colour) is Signal.red не работает с ValueError: 'red' is not a valid Signal.

Ответы

Ответ 1

Не создается экземпляр Enum. Синтаксис Signal(foo) используется для доступа к членам Enum по значению, которые не предназначены для использования, когда они auto().

Однако можно использовать строку в доступ к элементам Enum, чтобы получить доступ к значению в dict, используя квадратные скобки:

Signal[brain_detected_colour] is Signal.red

Другая возможность - сравнить строку с name члена Enum:

# Bad practice:
brain_detected_colour is Signal.red.name

Но здесь мы не проверяем идентификацию между членами Enum, а сравниваем строки, поэтому лучше использовать тест равенства:

# Better practice:
brain_detected_colour == Signal.red.name

(Сравнение идентичности строк, выполненных благодаря string interning, на что лучше не полагаться. Спасибо @mwchase и @Chris_Rands за чтобы я знал об этом.)

Еще одна возможность заключалась бы в том, чтобы явным образом задавать значения членов как их имена при создании Enum:

class Signal(Enum):
    red = "red"
    green = "green"
    orange = "orange"

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

Тогда Signal(brain_detected_colour) is Signal.red будет действительным.

Ответ 2

Возможно, что auto() возвращает имя члена перечисления как его значение (которое находится в разделе auto в документах 1

>>> class AutoName(Enum):
...     def _generate_next_value_(name, start, count, last_values):
...         return name
...

>>> class Ordinal(AutoName):
...     NORTH = auto()
...     SOUTH = auto()
...     EAST = auto()
...     WEST = auto()
...

>>> list(Ordinal)
[<Ordinal.NORTH: 'NORTH'>, <Ordinal.SOUTH: 'SOUTH'>, <Ordinal.EAST: 'EAST'>, <Ordinal.WEST: 'WEST'>]

1 Для этого требуется версия Python 3.6, или aenum 2.0 2 (aenum работает с Pythons как старое как 2.7).

2 Раскрытие информации: Я являюсь автором Python stdlib Enum, enum34 backport, а Расширенное перечисление (aenum) библиотека.