6

I have MyEnum, an enumerate derived from enum, defined in a myenum.py file such as :

# myenum.py
import enum

class MyEnum(enum.Enum):
    ONE = 1
    TWO = 2

Then, I import this file using the importlib.import_module() method. I create a an instance of my enumerate, and test its value : it is correct, as intended.

However, if I reload my file, using importlib.reload(), a is no longer equal to MyEnum.ONE. What is causing this ? I'm on Python 3.7.

# test.py
import importlib

def test_enum(e):
    print(e, myenum.MyEnum.ONE)
    print("test is :", e==myenum.MyEnum.ONE)

if __name__=='__main__':
    globals()["myenum"] = importlib.import_module("myenum")
    a = myenum.MyEnum.ONE
    test_enum(a)
    importlib.reload(globals()["myenum"])
    test_enum(a)

Result

MyEnum.ONE MyEnum.ONE
test is : True
MyEnum.ONE MyEnum.ONE
test is : False

Edit : After further research, it appears enums in Python are compared by IDs. However, when reimporting the module, the IDs of my enum are changed, which is why the comparison returns False.

What options would there be to avoid this ID change or allow the comparison to stay True ?

Kusefiru
  • 130
  • 7
  • If your enum values are only going to be integers, then I would suggest using [IntEnum](https://docs.python.org/3/library/enum.html#intenum), which allows comparison by value instead of identity – George D. Mar 03 '21 at 16:33
  • @GeorgeD. noooo. That *defeats the entire purpose of enums* – juanpa.arrivillaga Mar 11 '21 at 02:13
  • Note, even if they *didn't* compare by id's since you've reloaded the module, you've effectively have two *seperate types*, and most types consider the type itself in equality comparisons, e.g. `if isinstance(other, Foo): return self.value == other.value else return NotImplemented`. So this is always going to be problematic – juanpa.arrivillaga Mar 11 '21 at 02:15

2 Answers2

6
  1. It's impossible to avoid the ID change; you made a new class with new instances while instances of the existing class still existed; definitionally, the IDs aren't allowed to be equal.

  2. If you must allow them to compare equal despite differing identities, you can just override __eq__ on the enum to do whatever you like, e.g.:

    class MyEnum(enum.Enum):
        ONE = 1
        TWO = 2
        def __eq__(self, other):
            if type(self).__qualname__ != type(other).__qualname__:
                return NotImplemented
            return self.name == other.name and self.value == other.value
    
        def __hash__(self):
            return hash((type(self).__qualname__, self.name))
    

    I don't recommend this (proper use of enums will use is for comparisons, and nothing can make that work, since it relies on the IDs, which per point #1, will always differ), and it's going to be much slower (removing one of the benefits of enums), but it's doable if you must allow reloading during development. Just make sure to remove the reload for the production code.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
1

You cannot guarantee that MyEnum will always refer to the same class. But the instance e itself always refers the same class. Therefore, a workaround is to change your test to:

print("test is :", e==type(e).ONE)
udifuchs
  • 351
  • 2
  • 5