How to compare Enums in Python? How to compare Enums in Python? python python

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