2.2. OOP Hash

  • Used for quickly compare two objects

  • All objects compare unequal (except with themselves)

  • set() elements has to be hashable

  • dict() keys has to be hashable

  • Used to quickly compare dictionary keys during a dictionary lookup

  • Since Python 3.11: siphash13 is added as a new internal hashing algorithms. It has similar security properties as siphash24 but it is slightly faster for long inputs. str, bytes, and some other types now use it as default algorithm for hash() [1]

class MyClass:
    def __hash__(self, *args, **kwargs):
        return 1234

    def __eq__(self, other):
        return hash(self) == hash(other)

2.2.1. Hash Method

  • hash(obj) ->  int

  • hash() returns the hash value of the object (if it has one)

  • __hash__ should return the same value for objects that are equal

  • User-defined classes have __eq__() and __hash__() methods by default

  • It also shouldn't change over the lifetime of the object

  • Generally you only implement it for immutable objects

class User:
    def __init__(self, firstname, lastname):
        self.firstname = firstname
        self.lastname = lastname

    def __hash__(self, *args, **kwargs):
        firstname = hash(self.firstname)
        lastname = hash(self.lastname)
        return hash(firstname + lastname)

    def __eq__(self, other):
        return hash(self) == hash(other)

2.2.2. Examples

dict() keys has to be hashable:

data = {}

data[1] = 'whatever'
data[1.1] = 'whatever'
data['a'] = 'whatever'
data[True] = 'whatever'
data[False] = 'whatever'
data[None] = 'whatever'

data[(1,2)] = 'whatever'

data[[1,2]] = 'whatever'
# Traceback (most recent call last):
# TypeError: unhashable type: 'list'

data[{1,2}] = 'whatever'
# Traceback (most recent call last):
# TypeError: unhashable type: 'set'

data[frozenset({1,2})] = 'whatever'

data[{'a':1}] = 'cokolwiek'
# Traceback (most recent call last):
# TypeError: unhashable type: 'dict'

set() elements must be hashable:

{1, 1, 2}
# {1, 2}

{1, 1.1, 'a'}
# {1, 1.1, 'a'}

{'a', (1, 2)}
# {'a', (1, 2)}

2.2.3. Set Definition

>>> data = {'one', 'two', 'three'}
>>>
>>> data  
{'two', 'one', 'three'}
>>>
>>> data  
{'two', 'one', 'three'}
>>>
>>> data  
{'two', 'one', 'three'}
>>>
>>> data  
{'two', 'one', 'three'}
>>>
>>>
>>> hash('one')  
8582268235435668451
>>>
>>> hash('two')  
-6866033106748034186
>>>
>>> hash('three')  
302666503681750062
class User:
    def __init__(self, name):
        self.name = name


mark = User('Mark Watney')
data = {mark, mark}
print(data)
# {<__main__.User object at 0x105be25b0>}

data = {User('Mark Watney'), User('Mark Watney')}
print(data)
# {<__main__.User object at 0x105be20a0>,
#  <__main__.User object at 0x105be2040>}
class User:
    def __init__(self, name):
        self.name = name

    def __hash__(self):
        return hash(self.name)

    def __eq__(self, other):
        return hash(self) == hash(other)


mark = User('Mark Watney')
data = {mark, mark}
print(data)
# {<__main__.User object at 0x105bc77c0>}

data = {User('Mark Watney'), User('Mark Watney')}
print(data)
# {<__main__.User object at 0x105bc7700>}

2.2.4. Problem

>>> hash(-1) == hash(-2)
True
>>> hash(-1)
-2
>>>
>>> hash(-2)
-2

2.2.5. Use Case - 1

class User:
    def __init__(self, name):
        self.name = name


data = set()
mark = User('Mark Watney')

data.add(mark)
print(data)
# {<__main__.User object at 0x105bde070>}

data.add(mark)
print(data)
# {<__main__.User object at 0x105bde070>}

2.2.6. Use Case - 2

class User:
    def __init__(self, name):
        self.name = name


data = set()

data.add(User('Mark Watney'))
print(data)
# {<__main__.User object at 0x105bc7d00>}

data.add(User('Mark Watney'))
print(data)
# {<__main__.User object at 0x105bc7d00>,
#  <__main__.User object at 0x105bc7e20>}

2.2.7. Use Case - 3

class User:
    def __init__(self, name):
        self.name = name

    def __hash__(self):
        return hash(self.name)

    def __eq__(self, other):
        return hash(self) == hash(other)


data = set()

data.add(User('Mark Watney'))
print(data)
# {<__main__.User object at 0x105bde9d0>}

data.add(User('Mark Watney'))
print(data)
# {<__main__.User object at 0x105bde9d0>}

2.2.8. Hashable

key = list([1, 2, 3])
hash(key)
# Traceback (most recent call last):
# TypeError: unhashable type: 'list'
class list(list):
    def __hash__(self):
        return 0

key = list([1, 2, 3])
hash(key)
0
data = {}

key = list([1,2,3])
data[key] = 'whatever'
# Traceback (most recent call last):
# TypeError: unhashable type: 'list'

class list(list):
    def __hash__(self):
        return 0

data[key] = 'whatever'
data
# {[1, 2, 3]: 'whatever'}

2.2.9. References