I will contend that because of the definition of __hash__ as it is known today you necessarily cannot have an implementation that is both "correct" -and- "good" at the same time. So instead, let's settle on "good" (since the accepted answer instead aims to be "correct"):
class Thing:
def __hash__(self):
return id(self)
def __eq__(self, other):
# it is advised to implement __eq__ when you implement __hash__
# however this __eq__ impl has nothing to do with this being a
# "good" implementation of __hash__, it merely ensures correctness
# of behavior for use in container types. you could obviously write
# something more tailored to your class than this, this is merely
# inheritance-friendly.
if not isinstance(other, type(self)):
return False
for aname in (set(dir(self)) | set (dir(other))):
if aname.startswith('__'):
continue
if (not hasattr(self, aname)) or (not hasattr(other, aname)):
return False
atype = type(self.attr)
if atype is types.MethodType or atype is types.FunctionType:
continue
if getattr(self, aname) != getattr(other, aname):
return False
return True
For this implementation __hash__ yields a value that is unique over the entire process for the lifetime of the object. For objects stored to a list or set (where reachability prevents object destruction) this is acceptable.
When used as a key for a dict, if the object is no longer reachable and is destroyed this key "may" collide. We must also consider that __hash__ results from other objects "may" collide as well due to differences in implementations. For these reasons implementations of containers typically check for object equality (both in Python and in other languages, as the notion of object hashes for container identity is not a uniquely Python concept.)
There are ways of defeating the problem of collision without depending on __eq__ but that is best reserved for another Q&A, and really deserves the consideration of container devs not the developers using their containers.
In any case, this implementation has the value of efficiency and simplicity, and does not suffer from collisions resulted from relying on the __hash__ of "correct" implementations such as that of Tuple. It is ideal that object identity is firstly resolved by __hash__ result comparison avoiding an additional dispatch for a __eq__ result.
To demonstrate this implementation, consider the following subclass and-also a unittest class for verification:
class SubThing(Thing):
def __init__(self, val1, val2):
if val1 != None:
self.val1 = val1
if val2 != None:
self.val2 = val2
import unittest
class ThingTests(unittest.TestCase):
def test_Things(self):
t1 = Thing()
t2 = Thing()
self.assertEqual(t1, t2)
set1 = set()
set1.add(t1)
set1.add(t2)
set1.add(t1)
set1.add(t2)
self.assertEqual(2, len(set1))
t3 = SubThing(1,2)
t4 = SubThing(1,2)
t5 = SubThing(2,3)
t6 = SubThing(None,None)
t7 = SubThing(None,5)
t8 = SubThing(5,None)
set2 = set()
set2.add(t3)
set2.add(t4)
set2.add(t5)
set2.add(t6)
set2.add(t7)
set2.add(t8)
self.assertEqual(6, len(set2))
self.assertEqual(t1, t2)
self.assertEqual(t3, t4)
self.assertNotEqual(t1, t3)
self.assertNotEqual(t3, t4)
self.assertNotEqual(t4, t5)
self.assertNotEqual(t5, t6)
self.assertNotEqual(t6, t7)
self.assertNotEqual(t7, t8)
Test passes.
Conclusion
This implementation is "good" but it is not "correct", why?
Because the current Python spec defines __hash__ with the constraint that two objects which would return true in an equality test must also return the same hash value. Bad. This will likely plague Python until the end of time. That doesn't mean you need to write naive code. The above will comply with all but the worst container implementations (those which foolishly rely on object equality instead of object hashes), and if there is a container implementation that misbehaves because of the above __hash__ then that impl it deserves to be called out and fixed.