How to compare a string with a python enum?
PythonEnumsPython 3.6Python Problem Overview
I just discovered the existence of an Enum base class in python and I'm trying to imagine how it could be useful to me.
Let's say I define a traffic light status:
from enum import Enum, auto
class Signal(Enum):
red = auto()
green = auto()
orange = auto()
Let's say I receive information from some subsystem in my program, in the form of a string representing a colour name, for instance brain_detected_colour = "red"
.
How do I compare this string to my traffic light signals?
Obviously, brain_detected_colour is Signal.red
is False
, because Signal.red
is not a string.
Signal(brain_detected_colour) is Signal.red
fails with ValueError: 'red' is not a valid Signal
.
Python Solutions
Solution 1 - Python
One does not create an instance of an Enum.
The Signal(foo)
syntax is used to access Enum members by value, which are not intended to be used when they are auto()
.
However one can use a string to access Enum members like one would access a value in a dict
, using square brackets:
Signal[brain_detected_colour] is Signal.red
Another possibility would be to compare the string to the name
of an Enum member:
# Bad practice:
brain_detected_colour is Signal.red.name
But here, we are not testing identity between Enum members, but comparing strings, so it is better practice to use an equality test:
# Better practice:
brain_detected_colour == Signal.red.name
(The identity comparison between strings worked thanks to string interning, which is better not to be relied upon. Thanks @mwchase and @Chris_Rands for making me aware of that.)
Yet another possibility would be to explicitly set the member values as their names when creating the Enum:
class Signal(Enum):
red = "red"
green = "green"
orange = "orange"
(See this answer for a method to have this automated.)
Then, Signal(brain_detected_colour) is Signal.red
would be valid.
Solution 2 - Python
A better practice is to inherit Signal
from str
:
class Signal(str, Enum):
red = 'red'
green = 'green'
orange = 'orange'
brain_detected_colour = 'red'
brain_detected_colour == Signal.red # direct comparison
Solution 3 - Python
It is possible to have auto()
return the name of the enum member as its value (which is in the auto
section of the docs1:
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()
and in use:
>>> list(Ordinal)
[<Ordinal.NORTH: 'NORTH'>, <Ordinal.SOUTH: 'SOUTH'>, <Ordinal.EAST: 'EAST'>, <Ordinal.WEST: 'WEST'>]
1 This requires version Python 3.6, or aenum
2.02 (aenum
works with Pythons as old as 2.7).
2 Disclosure: I am the author of the Python stdlib Enum
, the enum34
backport, and the Advanced Enumeration (aenum
) library.
Solution 4 - Python
class Signal(Enum):
red = auto()
green = auto()
orange = auto()
def equals(self, string):
return self.name == string
brain_detected_colour = "red"
if Signal.red.equals(brain_detected_colour):
#something awesome