Hash method is defaulted in python 2, not python 3

2019-07-27 10:20发布

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?

标签: python hash
2条回答
叛逆
2楼-- · 2019-07-27 10:25

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.

查看更多
倾城 Initia
3楼-- · 2019-07-27 10:41

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.

查看更多
登录 后发表回答