How to compare Enums in Python?
You should always implement the rich comparison operaters if you want to use them with an Enum
. Using the functools.total_ordering
class decorator, you only need to implement an __eq__
method along with a single ordering, e.g. __lt__
. Since enum.Enum
already implements __eq__
this becomes even easier:
>>> import enum>>> from functools import total_ordering>>> @total_ordering... class Grade(enum.Enum):... A = 5... B = 4... C = 3... D = 2... F = 1... def __lt__(self, other):... if self.__class__ is other.__class__:... return self.value < other.value... return NotImplemented... >>> Grade.A >= Grade.BTrue>>> Grade.A >= 3Traceback (most recent call last): File "<stdin>", line 1, in <module>TypeError: unorderable types: Grade() >= int()
Terrible, horrible, ghastly things can happen with IntEnum
. It was mostly included for backwards-compatibility sake, enums used to be implemented by subclassing int
. From the docs:
For the vast majority of code, Enum is strongly recommended, since IntEnum breaks some semantic promises of an enumeration (by being comparable to integers, and thus by transitivity to other unrelated enumerations). It should be used only in special cases where there’s no other choice; for example, when integer constants are replaced with enumerations and backwards compatibility is required with code that still expects integers.
Here's an example of why you don't want to do this:
>>> class GradeNum(enum.IntEnum):... A = 5... B = 4... C = 3... D = 2... F = 1... >>> class Suit(enum.IntEnum):... spade = 4... heart = 3... diamond = 2... club = 1... >>> GradeNum.A >= GradeNum.BTrue>>> GradeNum.A >= 3True>>> GradeNum.B == Suit.spadeTrue>>>
I hadn'r encountered Enum before so I scanned the doc (https://docs.python.org/3/library/enum.html) ... and found OrderedEnum (section 8.13.13.2) Isn't this what you want? From the doc:
>>> class Grade(OrderedEnum):... A = 5... B = 4... C = 3... D = 2... F = 1...>>> Grade.C < Grade.ATrue
Combining some of the above ideas, you can subclass enum.Enum to make it comparable to string/numbers and then build your enums on this class instead:
import numbersimport enumclass EnumComparable(enum.Enum): def __gt__(self, other): try: return self.value > other.value except: pass try: if isinstance(other, numbers.Real): return self.value > other except: pass return NotImplemented def __lt__(self, other): try: return self.value < other.value except: pass try: if isinstance(other, numbers.Real): return self.value < other except: pass return NotImplemented def __ge__(self, other): try: return self.value >= other.value except: pass try: if isinstance(other, numbers.Real): return self.value >= other if isinstance(other, str): return self.name == other except: pass return NotImplemented def __le__(self, other): try: return self.value <= other.value except: pass try: if isinstance(other, numbers.Real): return self.value <= other if isinstance(other, str): return self.name == other except: pass return NotImplemented def __eq__(self, other): if self.__class__ is other.__class__: return self == other try: return self.value == other.value except: pass try: if isinstance(other, numbers.Real): return self.value == other if isinstance(other, str): return self.name == other except: pass return NotImplemented