__hash__

Qu'est-ce que la méthode spéciale __hash__ en Python ?

Qu'est-ce que la méthode __hash__ ?

La méthode spéciale __hash__ renvoie un entier : le hash de l'objet. Quand vous utilisez la fonction hash(mon_objet), Python appelle objet.__hash__() en arrière-plan.

Cet entier sert de numéro d'emplacement qu'utilisent les dictionnaires et les ensembles (sets) pour classer leurs éléments et les retrouver instantanément sans tous les parcourir.

Par défaut, tous les objets des classes que vous créez sont hashables : Python calcule le hash depuis l'emplacement en mémoire de l'objet.

class Utilisateur:
    def __init__(self, nom):
        self.nom = nom

u1 = Utilisateur("Patrick")
u2 = Utilisateur("Patrick")

print(hash(u1))            # Un entier
print(hash(u1) == hash(u1)) # True
print(hash(u1) == hash(u2)) # False : deux objets, deux identités
PYTHON
Un instant

Créez un compte pour exécuter ce code

Inscrivez-vous gratuitement pour modifier et exécuter du code Python directement dans votre navigateur.

Quels types sont hashables en Python ?

Pour les types intégrés de Python : les types immuables sont hashables et les types muables ne le sont pas.

# Hashables
print(hash(42))
print(hash("Patrick"))
print(hash((1, 2)))

# Non hashables
hash([1, 2])    # TypeError: unhashable type: 'list'
hash({"a": 1})  # TypeError: unhashable type: 'dict'
hash({1, 2})    # TypeError: unhashable type: 'set'
PYTHON

Si le hash est calculé à partir d'un contenu qui peut changer, il change aussi et l'objet devient introuvable à l'endroit où il est rangé. Nous en faisons la démonstration dans le glossaire sur __eq__.

Attention

Un tuple est immuable, mais son contenu ne l'est pas forcément. Ce qui veut dire qu'un tuple n'est hashable que si tous les éléments qu'il contient le sont !

hash(("Patrick", [1, 2]))  # TypeError: unhashable type: 'list'
PYTHON

La relation entre __hash__ et __eq__

Les deux méthodes sont indissociables : deux objets égaux doivent avoir le même hash. C'est pour cette raison que Python désactive __hash__ dès que vous définissez __eq__. Pour en comprendre le fonctionnement complet, tout est expliqué dans le glossaire sur __eq__.

Petite information amusante :

print(1 == 1.0 == True)  # True !

print(hash(1))     # 1
print(hash(1.0))   # 1
print(hash(True))  # 1

print({1, 1.0, True})  # {1}
PYTHON
Un instant

Créez un compte pour exécuter ce code

Inscrivez-vous gratuitement pour modifier et exécuter du code Python directement dans votre navigateur.

L'entier, le nombre à virgule et le booléen sont égaux et partagent donc le même hash.

Pourquoi le hash d'une chaîne change entre deux exécutions ?

Si vous exécutez ce code plusieurs fois, vous verrez que le hash n'est jamais le même : print(hash("Patrick")). Par sécurité, depuis Python 3.3, à chaque démarrage de l'interpréteur, Python applique une randomisation des hash pour les chaînes de caractères et les bytes.

Avant la randomisation, hash("abc") renvoyait la même valeur sur toutes les machines. On pouvait donc calculer à l'avance des milliers de chaînes ayant le même hash et les envoyer pour ralentir Python.

Bravo, tu es prêt à passer à la suite

Rechercher sur le site

Inscris-toi à Docstring

Pour commencer ton apprentissage.

Tu as déjà un compte ? Connecte-toi.