1

I've come across an example of weird behavior when transitioning some code from python 2 to python 3. Below's a minimal (?) example of it:


class Bar(object):
    def __init__(self, x):
        self.x = x
    def __eq__(self, other):
        return self.x == other.x

b = Bar(1)
print(hash(b))

when run with python2, this code produces some output (a hash of Bar(1)), while python3 causes a TypeError: unhashable type: 'Bar'

this means that __hash__ is somehow inherited (from object ?) in python 2.

So, my questions are: what is the hash of Bar(1) in python 2? And why is the behaviour different?

2 Answers 2

4

Yes, the data model has changed. In Python 3:

User-defined classes have __eq__() and __hash__() methods by default; with them, all objects compare unequal (except with themselves) and x.__hash__() returns an appropriate value such that x == y implies both that x is y and hash(x) == hash(y).

A class that overrides __eq__() and does not define __hash__() will have its __hash__() implicitly set to None. When the __hash__() method of a class is None, instances of the class will raise an appropriate TypeError when a program attempts to retrieve their hash value, and will also be correctly identified as unhashable when checking isinstance(obj, collections.abc.Hashable).

So, since you've defined a __eq__ explicitely, but did not define a __hash__, Python 3 objects will implicitely have __hash__ = None, causing the objects to be unhashable

In Python 2:

User-defined classes have __cmp__() and __hash__() methods by default; with them, all objects compare unequal (except with themselves) and x.__hash__() returns a result derived from id(x).

So it is hashing based on identity, which is a problem, because it isn't consistent with your __eq__. This is a reason that Python 3 switched behaviors.

Sign up to request clarification or add additional context in comments.

Comments

2

From https://docs.python.org/3/reference/datamodel.html#object.hash

A class that overrides eq() and does not define hash() will have its hash() implicitly set to None. When the hash() method of a class is None, instances of the class will raise an appropriate TypeError when a program attempts to retrieve their hash value, and will also be correctly identified as unhashable when checking isinstance(obj, collections.abc.Hashable).

See https://docs.python.org/2/reference/datamodel.html#object.hash for the Python 2 version.

2 Comments

The second link only says "x.__hash__() returns a result derived from id(x).". How do I see what how the result is derived?
You can see some discussions of that regarding different versions of Python in the comments here: stackoverflow.com/questions/11324271/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.