{{tag>cours python dev dictionnaire todo}} ====== Python: les dictionnaires ====== Dans cette vidéo, nous allons voir l'utilisation des **dictionnaires**en Python. Les dictionnaires sont des tables de hash, on a donc un temps d'accès, d'insertion d'effacement et un test d'appartenance qui sont indépendants du nombre d'éléments. De plus, les dictionnaires sont des objets mutables, ça veut dire qu'on peut les modifier en place avec donc une excellent efficacité mémoire. Dans un dictionnaire, on peut avoir comme clé n'importe quel objet qui est hashable. Qu'est-ce que c'est, **un objet hashable**? C'est un objet sur lequel on peut calculer cette fameuse fonction de hash. Pour l'instant, sachez qu'en Python **tous les objets immuables sont hashables**; et que **tous les objets mutables ne sont pas hashables**. Quelle est l'intuition derrière ça ? C'est que vous vous souvenez que la fonction de hash doit faire un calcul sur la clé. Or, si cette clé change en cours d'exécution, la fonction de hash va faire un calcul qui va être différent et par conséquent, votre table de hash va devenir inconsistante. C'est pourquoi en Python avec les types built-in, seuls les types immuables c'est-à-dire qui ne peuvent plus changer une fois qu'ils ont été créés, peuvent être utilisés comme **clés d'un dictionnaire**. Ouvrons maintenant un interpréteur Python pour commencer à jouer avec les dictionnaires. Pour créer un dictionnaire, on utilise la notation accolades. Regardons cet exemple. Je vais écrire *age = {}*et j'ai bien créé un objet qui est de type *dict*, le type dictionnaire. Ensuite, je peux créer un dictionnaire en écrivant directement à la main des couples clé - valeur séparés par des : donc 'ana' : 35 virgule 'eve' : 30 virgule 'bob' : 38 Donc là, j'ai créé un dictionnaire qui a trois clés 'ana', 'eve' et 'bob', et trois valeurs 35, 30 et 38. Vous pouvez vraiment voir les dictionnaires comme une collection de couples clé - valeur. Votre dictionnaire va stocker cette collection qui n'est pas ordonnée, il n'y a pas d'ordre dans un dictionnaire, vous allez avoir le couple 'ana' : 35, le couple 'eve' : 30 et le couple 'bob' : 38. Ensuite, votre dictionnaire va vous permettre d'accéder aux différents éléments en nommant la clé, par exemple, 'ana' va me permettre d'accéder à la valeur correspondant à la clé 'ana', age['ana'] je vois bien apparaître 35. J'ai une deuxième manière de construire un dictionnaire, c'est la manière suivante: Supposons déjà, supposons qu'au départ vous ayez une liste qui contienne des tuples clé - valeur. ('ana', 35), un deuxième tuple: ('eve', 30) et un troisième tuple: ('bob', 38). J'ai donc une liste qui contient des tuples. Or, je vous ai expliqué qu'en Python la liste est vraiment au coeur de tous vos programmes donc c'est tout à fait possible de vous retrouver à un moment avec une liste qui contient des couples de clés et valeurs. À ce moment-là, pour créer un dictionnaire à partir de cette liste de tuples, c'est très simple, vous n'avez qu'à écrire la chose suivante: age égale dict de a. La fonction *built-in dict*va créer un dictionnaire à partir de ces couples clé - valeur où vous aurez: 'ana' qui correspond à la valeur 35, 'eve' à la valeur 30 et 'bob' à la valeur 38. De nouveau, je peux tout à fait accéder à la valeur correspondant à 'bob' ; a de 'bob' je vois que... alors, ce n'est pas a mon dictionnaire, a, c'était la liste. Je refais age de 'bob', vous remarquez au passage que l'exception est tout à fait explicite, j'ai TypeError: list indices must be integers or slices ; l'objet que je voulais manipuler n'était pas une liste mais un dictionnaire ; c'est ce qui me permet de savoir que j'ai fait une erreur. age de 'bob' me retourne 38 ; je réaffiche mon dictionnaire age ; et comme mon dictionnaire est mutable, je peux tout à fait changer la valeur correspondant à 'bob'. Je peux écrire maintenant 'bob' égale 45, et je regarde mon dictionnaire, la valeur correspondant à 'bob' a bien été modifiée. De nouveau, j'insiste bien sur le fait que les dictionnaires ne sont pas ordonnés ; lorsqu'on débute en Python on peut avoir l'impression que l'on a un certain ordre et que cet ordre peut changer par moment, en fait, vous n'avez aucune garantie d'ordre lorsque vous faites print d'un dictionnaire l'ordre affiché sera quelconque. Vous pouvez également effacer un couple clé - valeur, donc une entrée dans un dictionnaire, en utilisant l'instruction *del*. Si je fais del age de 'bob' je vais effacer la clé 'bob' et la valeur correspondante et j'obtiens donc mon dictionnaire qui maintenant ne contient plus que 'ana' : 35, 'eve' : 30. Une caractéristique très importante des dictionnaires, c'est que les dictionnaires ont des opérations qui sont très proches des séquences lorsque cette opération a un sens. Par exemple, combien est-ce que j'ai de clés dans mon dictionnaire ? Combien de couples clé - valeur ? J'utilise la *fonction built-in len*, qui va me retourner 2. Je peux également faire du test d'appartenance sur un dictionnaire ; encore une fois, je vais utiliser exactement la même instruction que pour les séquences, *l'instruction in*; donc je peux faire est-ce que 'ana' in mon dictionnaire age ? La réponse est "vrai". Est-ce que 'bob' in mon dictionnaire age ? La réponse est "faux". Vous voyez vraiment ce souci d'uniformité en Python ; lorsque c'est possible, on utilise quel que soit le type des instructions qui sont les mêmes. Par exemple, le nombre d'éléments dans une liste ou le nombre de clés dans un dictionnaire, c'est len ; le test d'appartenance sur toutes les séquences et sur les dictionnaires, c'est in. Pour finir, j'aimerais vous montrer comment accéder aux clés, aux valeurs, ou aux couples clé - valeur qu'on appelle les items en Python, d'un dictionnaire. J'ai mon dictionnaire qui s'appelle age, je veux accéder aux clés, je vais y accéder avec **la méthode keys()**. Et keys() va me retourner un objet qui contient les clés. Ensuite, je peux accéder aux valeurs avec values() qui va me retourner un objet qui contient les valeurs. Et je peux accéder aux couples clé - valeur qu'on appelle les items avec la méthode sur le dictionnaire items(). Et on voit que j'obtiens des couples clé - valeur. Ces méthodes *keys, values et items*retournent un objet qui est un petit peu particulier ; c'est un objet qu'on appelle **une vue**. Qu'est-ce que c'est, une vue, en Python ? C'est un objet sur lequel on peut itérer, donc on peut faire une boucle for sur cet objet, et on peut également faire un test d'appartenance, donc faire par exemple *in*directement sur cette vue ; la caractéristique principale des vues est que la vue est mise à jour en même temps que le dictionnaire. Regardons un exemple. Je vais écrire a égale age.keys(), je vais plutôt l'appeler *k*pour rendre plus explicite que c'est la vue sur les clés : *k = age.keys()*. Regardons k, je vois bien que c'est la vue qui me permet d'accéder aux clés. Et maintenant, je vais modifier mon dictionnaire age ; je vais écrire 'bob', donc je rajoute une clé dans mon dictionnaire, 'bob' égale 25. Je rajoute un couple clé - valeur et on rajoute les couples clé - valeur avec cette notation: mon dictionnaire entre crochets la clé qu'on veut rajouter égale la nouvelle valeur. Cette notation vous permet de modifier une valeur existante ou alors de rajouter un nouveau couple clé - valeur. Je rajoute 'bob', et regardons maintenant ma vue. Je n'ai pas recréé ma vue, c'est la vue que j'ai créée avant l'ajout de cette clé, et bien, je vois que ma vue a automatiquement été mise à jour avec cette nouvelle clé. Donc en fait, les vues, vous devez vraiment voir ça comme une vue permanente sur votre objet, si votre objet est modifié, la vue va voir ce nouvel objet qui a été modifié. Je vous montre ce test d'appartenance sur les vues, si je regarde est-ce que 'ana' est dans mes clés ? C'est vrai. Est-ce que 'bill' est dans mes clés ? C'est faux. J'ai bien le test d'appartenance qui fonctionne tout à fait directement sur la vue. Pour finir, j'aimerais vous montrer comment est-ce qu'on parcourt les dictionnaires. Une manière tout à fait classique de parcourir les dictionnaires, j'ai mon dictionnaire age, c'est d'écrire la boucle *for*suivante, en utilisant la notation de **tuple unpacking**. Je vais faire for k, v in age.items() Que me retourne items ? Une vue sur les couples clé - valeur donc à chaque tour de boucle, la boucle for va retourner un tuple clé - valeur ; en faisant k, v in ce tuple, je fais du unpacking, donc k va correspondre à la clé, v va correspondre à la valeur. Et ensuite je peux faire un print de là, j'écris une f-string clé, valeur, et ici, j'écris directement k, v. J'exécute ce morceau de code et je vois effectivement apparaître mes couples clé - valeur. Pour finir, je veux juste vous montrer que *l'itérateur sur les dictionnaire*est un itérateur directement sur les clés. Si j'itère directement sur mon dictionnaire, sans spécifier de vue, ce que va me retourner la boucle for, c'est simplement une nouvelle clé. Ici, je fais un print de k et je vois donc que j'itère directement sur les clés. Nous venons donc de voir le nouveau type dictionnaire qui est une implémentation de table de hash qui permet l'accès, l'insertion, la modification, et le test d'appartenance, indépendamment du nombre d'éléments. Le dictionnaire est une structure de données extrêmement souple, qui vous permet par exemple d'implémenter sans aucun effort un agenda ou un annuaire.