Shobhit Shobhit - 14 days ago 9
Python Question

Interaction of a hashable class with Enum in Python

I have a defined a class which I am trying to make hashable. Additionally, there's an enum which uses objects of this class as values of its enum members.

from enum import Enum


class Dummy(object):
def __init__(self, name, property_):
self.name = name # can only be a string
self.property = property_ # can only be a string

def __hash__(self):
# print "Hash called for ", self
# print("----")
return hash(self.property)

def __eq__(self, other):
# print "Eq called for: "
# print self
# print other
return (self.property == other.property)

def __ne__ (self, other):
return not (self == other)

def __str__(self):
return (self.name + "$" + self.property)


class Property(Enum):
cool = Dummy("foo", "cool")
hot = Dummy("bar", "hot")


Although this works fine, I noticed -- by un-commenting the
print
statements -- that the
__hash__
and
__eq__
magic methods are invoked for the two enum member values. Why is this so? Aren't these used only during hashing and equality checks?

Additionally, if I change the enum class to the following, all hell breaks loose.

class Property(Enum):
cool = Dummy("foo", "cool")
hot = [Dummy("bar-1", "hot-1"), Dummy("bar-2", "hot-2")]


The
__eq__
magic method seems to be called for the
Dummy
object corresponding to
Property.cool
and the list corresponding to
Property.hot
, as evident from the output:

Hash called for foo$cool
----
Eq called for:
foo$cool
[<__main__.Dummy object at 0x7fd36633f2d0>, <__main__.Dummy object at 0x7fd36633f310>]
----
Traceback (most recent call last):
File "test.py", line 28, in <module>
class Property(Enum):
File "/blah/.local/lib/python2.7/site-packages/enum/__init__.py", line 237, in __new__
if canonical_member.value == enum_member._value_:
File "test.py", line 19, in __eq__
return (self.property is other.property)
AttributeError: 'list' object has no attribute 'property'


Why is this happening? Why are the magic methods called in the first place, and why is
__eq__
called on a class object and a list?

Please note that this is only a representative example, and the real use-case makes this design -- enum with values as lists of hashable class objects -- appear less weird.

Answer

The Enum class is comparing its member object values to see if any are aliases of another. For example, in the following Enum, both a and b represent the same value, so only a should show up in the member list (aliases don't):

class A(Enum):
    a=1
    b=1

You can verify this by looking at the source code for the line that did the equality check: source

For the hashing, this is done to provide a by-value lookup of the enum members. Again, this can be found in the source code