How to make an object properly hashable?

PythonPython 3.x

Python Problem Overview


Here is my code:

class Hero:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return self.name + str(self.age)

    def __hash__(self):
        print(hash(str(self)))
        return hash(str(self))

heroes = set()

heroes.add(Hero('Zina Portnova', 16)) # gets hash -8926039986155829407
print(len(heroes)) # gets 1

heroes.add(Hero('Lara Miheenko', 17)) # gets hash -2822451113328084695
print(len(heroes)) # gets 2

heroes.add(Hero('Zina Portnova', 16)) # gets hash -8926039986155829407
print(len(heroes)) # gets 3! WHY?

Why is this happening?
The 1st and the 3rd object have same content and same hash but len() tells about 3 unique objects?

Python Solutions


Solution 1 - Python

You also need to define __eq__() in a compatible way with __hash__() – otherwise, equality will be based on object identity.

On Python 2, it is recommended you also define __ne__ to make != consistent with ==. On Python 3, the default __ne__ implementation will delegate to __eq__ for you.

Solution 2 - Python

Here is the the entire code :

class Hero:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return self.name + str(self.age)

    def __hash__(self):
        print(hash(str(self)))
        return hash(str(self))
    
    def __eq__(self,other):
    	return self.name == other.name and self.age== other.age



heroes = set()
heroes.add(Hero('Zina Portnova', 16)) # gets hash -8926039986155829407
print(len(heroes)) # gets 1

heroes.add(Hero('Lara Miheenko', 17)) # gets hash -2822451113328084695
print(len(heroes)) # gets 2

heroes.add(Hero('Zina Portnova', 16)) # gets hash -8926039986155829407
print(len(heroes)) # gets 2 

The function recognises the __eq__ and as such the len is 2.

Solution 3 - Python

The Python documentation might be helpful:

>If a class does not define a __cmp__() or __eq__() method it should not define a __hash__() operation either;

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDenis KreshikhinView Question on Stackoverflow
Solution 1 - PythonSven MarnachView Answer on Stackoverflow
Solution 2 - PythonSeasonalShotView Answer on Stackoverflow
Solution 3 - PythonKshitij SaraogiView Answer on Stackoverflow