Python 2 Enum 不對等的問題

在 python2, enum34 中 enum 可能會因為 import 路徑而導致比較不對等的問題.

Python 3 會搜尋所有的 sys.path 的 root path. 因此在 Python2 中用不同的 import path 會導致同一個 module 互不相等的現象在 Python 3 中不會發生. PEP 328 | Issue30545

舉例來說:

File content and structure

1
2
3
4
5
/test_enum
  - /test_enum/__init__.py
               module_a.py
               module_b.py
               the_enum.py

the_enum.py

1
2
3
4
5
6
7
from enum import Enum

class Fruit(Enum):
    APPLE = 'apple'
    BANANA = 'banana'
    GUAVA = 'guava'
    PINEAPPLE = 'pineapple'

module_a.py

1
2
3
from . import module_b
from . import the_enum
from .the_enum import Fruit

module_b.py

1
from . import the_enum

Python2 vs Python3

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from enum_test import module_a, module_b, the_enum

# 同樣 import the_enum, 然後從它存取 Fruit
print(module_a.the_enum.Fruit.APPLE == module_b.the_enum.Fruit.APPLE)
# Python2: True, Python3:True


# 不同 import path
print(module_a.Fruit.APPLE == module_b.the_enum.Fruit.APPLE)
# Python2: False, Python3:True

參考

雖然 Python2 中不同 import path 導致物件不相等(id 不同), 但是他們的值是相同的, 所以 hash 這兩個不同 id 的物件會得到相同的 hash value, implement __eq__ 來比較 hash values 就可以得到正確的比較結果.

Customized equal.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from enum import Enum


class Fruit(Enum):
  APPLE = 'apple'
  BANANA = 'banana'
  GUAVA = 'guava'
  PINEAPPLE = 'pineapple'

  def __eq__(self, other):
    return hash(self) == hash(other)

Result

1
2
3
4
5
6
7
8
9
from enum_test import module_a, module_b, the_enum

print(module_a.the_enum.Fruit.APPLE == module_b.the_enum.Fruit.APPLE)
# Python2: True, Python3:True


# 雖然兩個 enum 還是因為不同的 path 而不相等, 但是 __eq__ 使用 member 的 hash 值比較, 所以還是相等.
print(module_a.Fruit.APPLE == module_b.the_enum.Fruit.APPLE)
# Python2: True, Python3:True