Comprendre les tables de hachage grâce aux dictionnaires Python

Pourquoi un dictionnaire retrouve-t-il une valeur presque instantanément ?

Denis Hulo

Objectif : découvrir les principes des tables de hachage, comprendre la gestion des collisions et voir comment ces mécanismes sont mis en œuvre dans les dictionnaires Python.

Niveau requis : bases en programmation Python et notions élémentaires de structures de données.

Complément : pour une étude plus approfondie des tables de hachage, de leurs algorithmes et de leurs différentes variantes, le lecteur pourra consulter l'article de Jean-Philippe Collette : Les tables de hachage.

Commentez cet article : Commentez

- Titre : Comprendre les tables de hachage grâce aux dictionnaires Python

- Auteur : Denis Hulo

- Parution : 29 juillet 2026

- Public visé :

- Licence : Le contenu de cet article est rédigé par Denis Hulo et est mis à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale 3.0 non transposé. Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright ® 2013 Developpez.com.

Sommaire

- I. Introduction

- II. Le problème de la recherche

- II-A. Recherche séquentielle dans une liste

- II-B. Vers un accès direct

- III. Principe des tables de hachage

- III-A. L'idée générale

- III-B. Du hash à l'indice

- III-C. Une limite inévitable

- IV. Gestion des collisions

- IV-A. Quand deux clés veulent la même case

- IV-B. Pourquoi les collisions sont inévitables

- IV-C. Le chaînage

- IV-D. L'adressage ouvert

- IV-E. L'adressage ouvert dans Python

- V. Organisation interne d'un dictionnaire Python

- V-A. Structure générale

- V-B. Insertion d'un élément

- V-C. Recherche d'un élément

- V-D. Suppression d'un élément

- VI. Complexité algorithmique

- VI-A. Cas moyen

- VI-B. Cas défavorable

- VI-C. Pourquoi ce cas est rare

- VII. Clés de dictionnaire et objets hashables

- VII-A. Quelles clés sont autorisées ?

- VII-B. Qu'est-ce qu'un objet hashable ?

- VII-C. Les méthodes __hash__() et __eq__()

- VII-D. Créer des objets hashables

- VIII. Particularités des dictionnaires Python

- VIII-A. Randomisation des hashes

- VIII-B. Ordre d'insertion

- VIII-C. Redimensionnement automatique

- IX. Bonnes pratiques

- IX-A. Choisir des clés immuables

- IX-B. Utiliser les méthodes adaptées

- IX-C. Comprendre le coût mémoire

- IX-D. Quand utiliser un dictionnaire ?

- X. Conclusion

- XI. Remerciements

I - Introduction

Les dictionnaires font partie des structures de données les plus utilisées en Python.

Ils permettent d'associer une valeur à une clé et d'accéder très rapidement aux informations stockées.

Par exemple :

notes = {
"Alice": 15,
"Bob": 18,
"Charlie": 12
}

print(notes["Bob"])

Ce code affiche immédiatement la valeur associée à la clé "Bob".

Avec seulement trois éléments, cette recherche paraît triviale. Comment Python parvient-il pourtant à conserver cette efficacité lorsque le dictionnaire contient des milliers, voire des millions d'entrées ?

La réponse repose sur une structure fondamentale en informatique : la table de hachage.

Les dictionnaires Python sont construits sur des tables de hachage optimisées. Les concepts présentés dans cet article sont également utilisés dans de nombreux autres langages de programmation.

Avant de découvrir le mécanisme du hachage, intéressons-nous au problème que cette structure cherche à résoudre.

II - Le problème de la recherche

II-A - Recherche séquentielle dans une liste

Considérons la structure suivante :

notes = [
("Alice", 15),
("Bob", 18),
("Charlie", 12)
]

Pour retrouver la note de Bob, il faut parcourir la liste jusqu'à trouver l'élément recherché :

for nom, note in notes:
if nom == "Bob":
print(note)

Cette approche est simple, mais nécessite potentiellement de parcourir toute la collection.

Si l'élément recherché est situé à la fin de la liste, toutes les entrées précédentes devront être examinées.

Le nombre d'opérations augmente donc avec la taille de la collection.

Pour une liste contenant :

- 10 éléments, il faudra au pire effectuer 10 comparaisons ;

- 1 000 éléments, jusqu'à 1 000 comparaisons ;

- 1 000 000 d'éléments, jusqu'à 1 000 000 de comparaisons.

En analyse algorithmique, on dit que cette recherche possède une complexité en :

Le temps nécessaire augmente proportionnellement à la taille de la collection.

Lorsque les données deviennent volumineuses, la recherche séquentielle peut devenir un goulot d'étranglement important.

II-B - Vers un accès direct

Une idée naturelle consiste à éviter complètement le parcours de la collection.

Si nous pouvions calculer directement l'emplacement d'une donnée à partir de sa clé, il ne serait plus nécessaire d'examiner les autres éléments.

La recherche ne dépendrait alors plus du nombre total d'éléments stockés.

C'est précisément le rôle des tables de hachage.

III - Principe des tables de hachage

Nous avons vu qu'une recherche séquentielle devient coûteuse lorsque le nombre de données augmente. Les tables de hachage apportent une solution élégante à ce problème en permettant de déterminer rapidement où une information doit être stockée ou retrouvée.

Pour comprendre leur fonctionnement, examinons d'abord le rôle joué par la fonction de hachage.

III-A - L'idée générale

Une table de hachage associe chaque clé à un emplacement dans une structure de stockage.

Pour cela, elle utilise une fonction particulière appelée fonction de hachage.

Celle-ci transforme une clé en un entier.

Par exemple :

hash("Bob")

renvoie un nombre entier calculé à partir de la chaîne de caractères.

Une fonction de hachage transforme une donnée de taille arbitraire en une valeur numérique de taille fixe.

Cette transformation est déterministe : une même donnée produit toujours la même valeur. En revanche, il n'est pas possible de retrouver la donnée d'origine à partir de son hash.

III-B - Du hash à l'indice

La valeur produite par l'algorithme de hachage est généralement très grande.

Comme une table de hachage ne contient qu'un nombre limité d'emplacements, cette valeur doit être ramenée à un indice compris entre 0 et la taille de la table moins un.

Une méthode couramment utilisée consiste à appliquer l'opérateur modulo. C'est cette approche que nous utiliserons dans les exemples qui suivent.

Pour illustrer le principe, supposons que :

Considérons maintenant une table contenant dix emplacements.

L'indice utilisé sera :

La donnée pourra alors être stockée à la position 2.

Le fonctionnement général d'une table de hachage peut être résumé ainsi :

Clé
|
v
"Bob"
|
v
Fonction de hachage
|
v
42
|
v
42 % 10
|
v
2
|
v
+---+---+---+---+---+---+---+---+---+---+
|   |   |Bob|   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+---+---+
0   1   2   3   4   5   6   7   8   9
|
v
18

La valeur de hachage n'est pas utilisée directement comme indice. Elle est convertie en un indice valide pour la table, ce qui permet d'associer chaque clé à un emplacement de stockage.

Lors d'une recherche, le même calcul est effectué pour retrouver rapidement cet emplacement, sans parcourir l'ensemble des données. Le temps d'accès est ainsi généralement proche de O(1), c'est-à-dire pratiquement constant quelle que soit la taille de la structure.

Pour aller plus loin

Certaines implémentations de tables de hachage choisissent une taille qui est toujours une puissance de deux (16, 32, 64, 128…).

Elles peuvent alors utiliser une opération binaire telle que hash & (taille - 1) pour calculer efficacement l'indice correspondant.

Ce détail d'implémentation ne change toutefois pas le principe présenté dans cet article : convertir une valeur de hachage en un indice exploitable dans la table.

III-C - Une limite inévitable

Le mécanisme présenté jusqu'à présent semble idéal : chaque clé est associée à un indice permettant d'accéder directement à son emplacement.

On pourrait alors imaginer que chaque clé dispose toujours de sa propre case dans la table.

Pourtant, cette situation n'est pas toujours possible.

Les tables de hachage doivent donc faire face à une difficulté importante qui influence directement leur conception et leurs performances.

IV - Gestion des collisions

Le fonctionnement d'une table de hachage semble idéal : chaque clé est associée à un emplacement calculé à partir de son hash. En pratique, plusieurs clés peuvent parfois conduire au même indice.

Ces situations, appelées collisions, sont inévitables et constituent l'un des principaux défis de la conception des tables de hachage.

IV-A - Quand deux clés veulent la même case

Une collision se produit lorsque deux clés différentes conduisent au même emplacement dans la table de hachage.

Supposons que :

Les deux clés souhaitent être stockées à la position 2.

La table ne pouvant contenir qu'une seule entrée par emplacement, il est nécessaire de mettre en œuvre une stratégie permettant de conserver les deux données.

IV-B - Pourquoi les collisions sont inévitables

Les collisions ne sont pas des erreurs de programmation.

Elles résultent directement d'une contrainte mathématique fondamentale.

Le nombre de clés pouvant être utilisées est généralement très supérieur au nombre d'emplacements disponibles dans une table de hachage.

Plusieurs clés différentes finiront donc inévitablement par partager un même indice.

Aucune fonction de hachage ne peut garantir l'absence totale de collisions.

L'objectif d'une bonne fonction de hachage n'est donc pas d'éliminer les collisions, mais de les rendre aussi rares que possible.

IV-C - Le chaînage

Une première solution consiste à autoriser plusieurs entrées à partager le même indice.

Chaque case de la table contient alors une liste d'éléments.

Si Bob et Tom produisent le même indice, ils seront stockés dans la même chaîne :

Index

0
1
2 -> Bob -> 18
Tom -> 16
3
4
5

Lors d'une recherche, il suffit d'accéder à la case correspondante puis de parcourir les éléments de la chaîne jusqu'à trouver la clé recherchée.

Cette technique est simple à mettre en œuvre et reste très utilisée dans de nombreuses implémentations.

IV-D - L'adressage ouvert

Une autre approche consiste à conserver toutes les entrées directement dans la table.

Lorsqu'une collision survient, un autre emplacement libre est recherché.

Supposons que Bob soit déjà stocké à l'indice 2 :

Index

0
1
2 -> Bob -> 18
3
4
5

Si Tom produit également l'indice 2, il pourra être placé dans une autre case disponible :

Index

0
1
2 -> Bob -> 18
3 -> Tom -> 16
4
5

Cette famille de techniques est appelée adressage ouvert.

Lors de la recherche de Tom, le système retrouve d'abord l'indice 2 puis explore une série d'emplacements déterminée par l'algorithme de sondage utilisé.

Une stratégie naïve consisterait à utiliser systématiquement la case suivante lorsqu'une collision survient.

On obtient alors progressivement des groupes d'éléments contigus :

Index

0
1
2 -> Bob
3 -> Tom
4 -> Eva
5 -> Paul
6 -> Marc

Ces regroupements, appelés clusters, augmentent le nombre de cases à examiner lors des recherches.

Les performances de la table peuvent alors se dégrader progressivement.

IV-E - L'adressage ouvert dans Python

Les dictionnaires Python utilisent une variante sophistiquée de l'adressage ouvert.

Lorsqu'une collision survient, Python ne se contente pas d'examiner systématiquement la case suivante.

Il calcule une séquence de positions à explorer à partir du hash de la clé. Cette séquence est déterministe : lors d'une recherche, Python suivra exactement le même chemin que lors de l'insertion.

Cette approche permet de répartir les éléments dans différentes zones de la table et de limiter la formation de regroupements d'entrées adjacentes.

Par exemple, après une collision à l'indice 2, les emplacements explorés ne seront pas nécessairement :

2 -> 3 -> 4 -> 5

mais pourront être répartis dans différentes zones de la table :

2 -> 7 -> 1 -> 9 -> ...

La séquence exacte dépend de l'algorithme interne utilisé par Python et peut évoluer entre les versions du langage.

La gestion efficace des collisions constitue l'une des principales raisons de la rapidité des dictionnaires Python.

Grâce à ces mécanismes, les opérations de recherche, d'insertion et de suppression conservent généralement une complexité moyenne proche de O(1).

Pour mieux comprendre l'origine de ces performances, examinons maintenant le fonctionnement interne d'un dictionnaire Python.

V - Organisation interne d'un dictionnaire Python

Nous connaissons désormais le principe général des tables de hachage. Voyons maintenant comment ces concepts sont mis en œuvre dans Python afin de permettre des insertions, des recherches et des suppressions particulièrement rapides.

Les détails d'implémentation sont complexes, mais les mécanismes fondamentaux peuvent être compris à l'aide d'un modèle simplifié.

V-A - Structure générale

Les dictionnaires Python reposent sur les principes des tables de hachage présentés dans les sections précédentes.

Chaque clé est associée à un emplacement calculé à partir de son hash, tandis que les mécanismes de gestion des collisions permettent de conserver de bonnes performances même lorsque plusieurs clés conduisent au même indice.

Voyons maintenant comment Python exploite ces principes lors des opérations d'insertion, de recherche et de suppression.

V-B - Insertion d'un élément

Considérons le dictionnaire suivant :

notes = {
"Alice": 15,
"Bob": 18
}

Ajoutons une nouvelle entrée :

notes["David"] = 14

Python réalise alors plusieurs opérations :

- calcul du hash de la clé ;

- détermination de l'indice correspondant dans la table ;

- vérification de la disponibilité de l'emplacement ;

- stockage de la paire clé-valeur.

Si l'emplacement calculé est déjà occupé par une autre clé, Python applique sa stratégie de gestion des collisions afin de trouver une position adaptée.

Ces opérations restent généralement très rapides et indépendantes du nombre total d'éléments présents dans le dictionnaire.

V-C - Recherche d'un élément

La recherche repose sur le même principe.

Lorsque l'on écrit :

notes["Bob"]

Python recalcule le hash de la clé puis détermine l'indice correspondant dans la table.

Si l'entrée recherchée se trouve directement à cet emplacement, la valeur est immédiatement retournée.

Dans le cas contraire, Python suit la même séquence de sondage que celle utilisée lors de l'insertion jusqu'à retrouver la clé recherchée ou conclure qu'elle est absente.

Il n'est donc pas nécessaire de parcourir l'ensemble des entrées du dictionnaire.

Le hash d'une clé reste constant durant toute sa durée de vie, ce qui permet à Python de reproduire exactement le même chemin de recherche que lors de l'insertion.

V-D - Suppression d'un élément

La suppression repose également sur le mécanisme de recherche.

del notes["Bob"]

Python localise d'abord l'entrée à supprimer en utilisant son hash et, si nécessaire, la même séquence de sondage que lors de son insertion.

Une fois l'élément trouvé, son emplacement est marqué de manière à préserver le bon fonctionnement des recherches futures.

La gestion de ces emplacements libérés participe au maintien des performances de la table de hachage.

Les mécanismes présentés jusqu'à présent expliquent comment les dictionnaires Python parviennent à retrouver rapidement une donnée.

Examinons maintenant le coût réel de ces opérations et leur comportement lorsque la taille de la structure augmente.

VI - Complexité algorithmique

Les tables de hachage sont largement utilisées en informatique en raison de leurs excellentes performances. Cependant, comme toute structure de données, leur efficacité dépend des conditions d'utilisation et de la manière dont les collisions sont gérées.

Examinons maintenant la complexité des principales opérations réalisées sur un dictionnaire Python.

VI-A - Cas moyen

Dans des conditions normales d'utilisation, les principales opérations sur un dictionnaire possèdent une complexité constante.

- Recherche : O(1)

- Insertion : O(1)

- Suppression : O(1)

Autrement dit, le temps nécessaire reste pratiquement identique que le dictionnaire contienne quelques dizaines ou plusieurs millions d'éléments.

Cette propriété explique pourquoi les dictionnaires sont omniprésents dans les programmes Python.

VI-B - Cas défavorable

Dans le pire des cas, de nombreuses collisions peuvent dégrader les performances.

La recherche peut alors nécessiter l'exploration successive de nombreux emplacements.

La complexité devient alors :

où n représente le nombre d'éléments stockés.

VI-C - Pourquoi ce cas est rare

En pratique, les fonctions de hachage utilisées par Python répartissent efficacement les clés dans la table.

De plus, Python redimensionne automatiquement les dictionnaires lorsque leur taux de remplissage devient trop important.

Cette opération permet de conserver suffisamment d'emplacements libres pour limiter les collisions.

Les performances observées en pratique sont donc très proches du comportement théorique en O(1).

Les dictionnaires semblent ainsi presque magiques du point de vue de l'utilisateur. Pourtant, certaines contraintes existent et expliquent pourquoi tous les objets ne peuvent pas être utilisés comme clés.

VII - Clés de dictionnaire et objets hashables

Les tables de hachage reposent sur une idée simple : une clé doit toujours conduire au même emplacement afin que la donnée puisse être retrouvée ultérieurement.

Pour garantir ce comportement, Python impose certaines contraintes aux objets utilisés comme clés dans les dictionnaires et les ensembles.

Comprendre ces contraintes permet de mieux saisir la notion d'objet hashable ainsi que le rôle joué par les mécanismes de hachage dans le langage.

VII-A - Quelles clés sont autorisées ?

Toutes les valeurs Python ne peuvent pas être utilisées comme clés dans un dictionnaire.

Par exemple, une chaîne de caractères est parfaitement valide :

notes = {
"Bob": 18
}

De même, les tuples peuvent servir de clés :

points = {
(10, 20): "A"
}

En revanche, les listes ne sont pas autorisées :

points = {
[10, 20]: "A"
}

Python produit alors l'erreur suivante :

TypeError: unhashable type: 'list'

Un tuple n'est hashable que si tous ses éléments le sont également. Ainsi, (1, [2]) ne peut pas être utilisé comme clé de dictionnaire, car la liste qu'il contient est modifiable.

Cette restriction est directement liée au fonctionnement des tables de hachage.

Pour comprendre pourquoi certaines valeurs peuvent servir de clés alors que d'autres sont refusées, il faut introduire la notion d'objet hashable.

VII-B - Qu'est-ce qu'un objet hashable ?

Un objet est dit hashable lorsqu'il possède une valeur de hachage stable durant toute sa durée de vie.

Autrement dit, le résultat obtenu avec la fonction hash() ne doit jamais changer.

Par exemple :

hash("Bob")
hash(42)
hash((1, 2))

Les chaînes de caractères, les entiers et les tuples possèdent une valeur de hachage exploitable par un dictionnaire.

Les objets dont le contenu ne peut plus être modifié après leur création sont dits immuables. Ils constituent généralement de bons candidats pour servir de clés, car leur valeur de hachage reste stable.

Une liste, en revanche, est modifiable :

coord = [10, 20]
coord.append(30)

Comme son contenu peut changer après sa création, sa valeur de hachage ne peut pas rester stable.

Python interdit donc l'utilisation des listes comme clés de dictionnaire.

Les clés d'un dictionnaire doivent posséder une valeur de hachage stable.

L'immuabilité concerne les objets, pas les variables. Une instruction comme x = x + 1 ou t = t + (3,) ne modifie pas l'objet existant : elle crée un nouvel objet, puis réaffecte la variable à celui-ci.

VII-C - Les méthodes __hash__() et __eq__()

Les objets Python définissent leur comportement de hachage grâce à la méthode spéciale __hash__().

Ils définissent également leur comportement de comparaison grâce à __eq__().

Ces deux mécanismes doivent rester cohérents.

Si deux objets sont considérés comme égaux, ils doivent produire le même hash :

Cette propriété est indispensable au bon fonctionnement des dictionnaires et des ensembles (set).

VII-D - Créer des objets hashables

Il est possible de définir ses propres objets utilisables comme clés.

Par exemple :

from dataclasses import dataclass

@dataclass(frozen=True)
class Point:
x: int
y: int

Les instances de cette classe pourront alors être utilisées comme clés dans un dictionnaire.

Dans la pratique, les classes immuables, dont les attributs ne peuvent plus être modifiés après la création de l'objet, sont souvent les meilleures candidates pour servir de clés. Les dataclasses gelées offrent notamment cette garantie.

VIII - Particularités des dictionnaires Python

Les principes présentés jusqu'à présent suffisent pour comprendre le fonctionnement général des tables de hachage et des dictionnaires.

Python met toutefois en œuvre plusieurs optimisations et mécanismes internes destinés à améliorer la sécurité, les performances et le confort d'utilisation.

VIII-A - Randomisation des hashes

Depuis plusieurs versions, Python introduit une part d'aléatoire dans le calcul des hashes de certaines chaînes de caractères.

Ainsi, le résultat suivant :

hash("Python")

peut varier d'une exécution à l'autre.

Cette mesure vise à protéger les applications contre certaines attaques exploitant les collisions de hachage.

VIII-B - Ordre d'insertion

Historiquement, les dictionnaires étaient considérés comme des collections non ordonnées.

Depuis Python 3.7, l'ordre d'insertion est garanti par le langage.

Par exemple :

d = {
"c": 3,
"a": 1,
"b": 2
}

print(d)

Produit :

{'c': 3, 'a': 1, 'b': 2}

Les clés apparaissent dans le même ordre que celui dans lequel elles ont été insérées.

Cette propriété est devenue une caractéristique officielle du langage Python depuis la version 3.7.

VIII-C - Redimensionnement automatique

Lorsqu'un dictionnaire devient trop rempli, Python augmente automatiquement la taille de sa table interne.

Les éléments existants sont alors redistribués dans une nouvelle table plus grande.

Cette opération est appelée rehashing.

Bien qu'elle soit relativement coûteuse, elle reste peu fréquente et permet de conserver de bonnes performances sur le long terme.

IX - Bonnes pratiques

Comprendre le fonctionnement interne des dictionnaires permet également de mieux les utiliser au quotidien.

Terminons ce tutoriel par quelques recommandations pratiques permettant de tirer pleinement parti de cette structure de données tout en évitant certains pièges courants.

IX-A - Choisir des clés immuables

Les objets immuables, c'est-à-dire dont le contenu ne peut plus être modifié après leur création, sont généralement les meilleurs candidats pour servir de clés :

- int ;

- float ;

- str ;

- tuple ;

- frozenset.

Leur stabilité garantit la cohérence de la table de hachage et permet de retrouver correctement les données associées.

IX-B - Utiliser les méthodes adaptées

Les dictionnaires fournissent plusieurs méthodes facilitant leur utilisation.

Par exemple :

compteur[mot] = compteur.get(mot, 0) + 1

permet de récupérer le contenu associé à une clé ou de retourner une valeur par défaut lorsque la clé est absente.

D'autres méthodes comme pop(), setdefault() ou update() simplifient également de nombreuses opérations courantes.

IX-C - Comprendre le coût mémoire

Les dictionnaires sont extrêmement rapides, mais cette efficacité a un coût.

Pour maintenir de bonnes performances, Python conserve des emplacements libres dans la table.

Cette structure occupe donc généralement davantage de mémoire qu'une simple liste contenant le même nombre d'éléments.

Comme souvent en informatique, les gains en vitesse s'obtiennent au prix d'une consommation mémoire plus importante.

IX-D - Quand utiliser un dictionnaire ?

Les dictionnaires sont particulièrement adaptés lorsque l'on souhaite :

- retrouver rapidement une information à partir d'une clé ;

- associer des valeurs à des identifiants, des noms ou des codes ;

- compter des occurrences ;

- mémoriser des résultats déjà calculés afin d'éviter des traitements répétitifs.

En revanche, lorsqu'un accès séquentiel suffit, une liste peut parfois être plus simple et plus économe en mémoire.

X - Conclusion

Les dictionnaires Python permettent de retrouver une information presque instantanément parmi des milliers, voire des millions d'autres.

Cette performance repose sur une idée simple, mais extrêmement puissante : transformer une clé en un emplacement de stockage afin d'accéder rapidement à l'information recherchée.

Les tables de hachage constituent aujourd'hui l'une des structures de données les plus importantes en informatique.

Leur efficacité explique leur présence dans de nombreux langages de programmation, bases de données et systèmes informatiques.

Derrière chaque table de hachage se cachent des mécanismes de calcul d'indices, de gestion des collisions et de nombreuses optimisations destinées à préserver l'efficacité des recherches.

Bien que les détails d'implémentation varient selon les langages et les bibliothèques, les principes fondamentaux restent les mêmes : répartir efficacement les données pour garantir un accès rapide à l'information.

Comprendre ces mécanismes permet non seulement d'utiliser les dictionnaires Python de manière plus efficace, mais également de mieux appréhender l'une des structures de données les plus importantes de l'informatique moderne.

XI - Remerciements

Je tiens à remercier christophe, fred1599 et Laurent Ott pour leur relecture, leurs remarques et leurs encouragements, qui ont contribué à améliorer cet article.

Je remercie également f-leb pour sa relecture orthographique attentive.

Sources et références

- Documentation Python : dictionnaires

- Python Data Model

- Hash Table

- Hash Function

- Python Time Complexity