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
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'
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'
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}
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.