Search code examples
pythonpython-3.xenums

Imported Enum class is not comparing equal to itself


In my code, I was puzzled by Enum instances with the same value not comparing equal. I quickly realized that their id(...) were different. Later, much less quickly, I realized that the only change since it last worked was the import statement: using explicit module path rather than relative.

Then I came up with this isolated example.

$ ls
e/

$ ls e
__init__.py  __pycache__/  m1.py  m1.pyc  m2.py

$  python3 --version
Python 3.5.1

$ cat e/m1.py
from enum import Enum
class E(Enum):
    x=0

$ cat e/m2.py
from m1 import E as E1
from e.m1 import E as E2

print(id(E1), id(E1.x))
print(id(E2), id(E2.x))

Let's run it:

$ PYTHONPATH=~/test python3 e/m2.py
41536520 42656096
41537464 42656488

Obviously, equality (which is by identity for Enum) does not hold. Anyone else out there find this troubling?

Let me elaborate. Adding two more files:

$ cat e/file1.py
from m1 import E

class C():
    def __init__(self):
        self.x = E.x

One more. Note the difference in imports.

$ cat e/file2.py
from e.m1 import E
from file1 import C

c = C()
print(c.x, E.x)
print('Surprise! ->', c.x is E.x)

Both are valid imports. Now run it.

$ PYTHONPATH=~/test python3 e/file2.py
E.x E.x
Surprise! -> False

How can I avoid this trap? Is there a reason why I should not be troubled by this?


Solution

  • Later, much less quickly, I realized that the only change since it last worked was the import statement: using explicit module path rather than relative.

    Python 3 doesn't have implicit relative imports. Your "relative" import is actually another absolute import. You're importing two completely distinct modules, m1 and e.m1, that happen to come from the same file.

    If you want to use a relative import in Python 3, you need to make it explicit:

    from .m1 import E
    #    ^ explicit relative import
    

    If you run the program as

    python3 -m e.m2
    

    you'll avoid the problems with the module search path that allowed both m1 and e.m1 to exist. You can also manually fix sys.path and set __package__ from e/m2.py.