:PROPERTIES:
:ID:       d1877b3b-3fe5-43d5-9be2-afb5ffe6b918
:mtime:    20220522095337
:ctime:    20220520230639
:END:
#+title: La méthode __hash__

* Synthaxe
#+BEGIN_SRC python
class.__hash__(self)
#+END_SRC
La méthode ~class.__hash__~ :
 * Est appelée par la /built-in method/ ~hash()~ et pour les opérations sur les membres des /hashed collection/ telles
   que /set/, /frozenset/ et /dict/,
 * Doit retourner un /int/,
 * Deux objet *égaux* doivent retourner la même valeur,
 * Si une classe *ne définit pas* de méthode ~__eq__~, elle ne doit pas définir de méthode ~__hash__~,
 * Si une classe *définit* une méthode ~__eq__~, mais pas de méthode ~__hash__~, ses instances :
   * Une méthode ~__hash__~ par défaut et retournant ~None~ est créée automatiquement,
   * Ne pourront pas être utilisées par les /hashable collections/,
 * Si une classe *définit* un objet /mutable/ et *définit* une méthode ~__eq__~, elle *ne doit pas* définir de méthode
   ~__hash__~, l'implémentation des /hashables collections/ nécessitant que la valeur des clés soit /immutable/,
 * Par défaut, les classes définit par l'utilisateur possèdent les méthodes ~__eq__~ et ~__hash__~ (~id(obj) >> 4~):
   tous les objets sont comparés comme non-égaux (sauf chaque objet avec lui-même).
   
Si ~a == b~, alors ~hash(a) == hash(b)~.
Si ~hash(a) == hash(b)~, alors ~a~ pourait être égal à ~b~.
Si ~hash(a) != hash(b)~, alors ~a != b~.

* Exemples

#+BEGIN_SRC python :results output
from __future__ import annotations

print(f'{hash(10)=}')
print(f'{hash("abc")=}')

class Dummy:

    def __init__(self, first: str, second: str) -> None:
        self.first = first
        self.second = second
        
class HashableDummy:

    def __init__(self, first: str, second: str) -> None:
        self.first = first
        self.second = second

    def __eq__(self, other: HashableDummy) -> bool:
        return (other is not None
                and isinstance(other, HashableDummy)
                and self.first == other.first
                and self.second == other.second)
    
    def __hash__(self) -> int:
        return hash((self.first, self.second))

    
dummy1, dummy2 = (Dummy("a", "b") for _ in range(2))
print(f'{hash(dummy1)=}')
print(f'{hash(dummy2)=}')
print(f'{dummy1 == dummy2=}')

hashable_dummy1, hashable_dummy2 = (HashableDummy("a", "b") for _ in range(2))
print(f'{hash(hashable_dummy1)=}')
print(f'{hash(hashable_dummy2)=}')
print(f'{repr(hashable_dummy1)=}')
print(f'{repr(hashable_dummy2)=}')
print(f'{hashable_dummy1 == hashable_dummy2=}')
#+END_SRC

#+RESULTS:
: hash(10)=10
: hash("abc")=-9106817633146148147
: hash(dummy1)=8779192445928
: hash(dummy2)=8779192413751
: dummy1 == dummy2=False
: hash(hashable_dummy1)=2529530665278513875
: hash(hashable_dummy2)=2529530665278513875
: repr(hashable_dummy1)='<__main__.HashableDummy object at 0x7fc10a69f4c0>'
: repr(hashable_dummy2)='<__main__.HashableDummy object at 0x7fc10a51a1c0>'
: hashable_dummy1 == hashable_dummy2=True

* Références
 * [[https://docs.python.org/3/reference/datamodel.html#object.__hash__][object.__hash__ - Docs Python]]
 * [[https://zestedesavoir.com/tutoriels/954/notions-de-python-avancees/1-starters/3-mutables-hashables/#3-3-hashables][Hashables - Zeste de savoir]]
 * [[https://eng.lyft.com/hashing-and-equality-in-python-2ea8c738fb9d][Hashing and equality in Python - Roy Williams]]

