Python >> Tutoriel Python >  >> Python

Dictionnaire Python - Le guide ultime

Python est livré avec plusieurs types de données intégrés. Ce sont les blocs de construction fondamentaux de toute la langue. Ils ont été optimisés et perfectionnés pendant de nombreuses années. Dans ce didacticiel complet, nous allons explorer l'un des plus importants :le dictionnaire (ou dict en abrégé).

Pour votre commodité, j'ai créé un livre électronique complet de 8 000 mots que vous pouvez télécharger directement au format PDF haute résolution (s'ouvre dans une nouvelle fenêtre).

Télécharger l'article au format PDF

Sauf indication contraire, j'utiliserai Python 3.8 tout au long. La fonctionnalité du dictionnaire a changé au cours des dernières versions de Python. Si vous utilisez une version autre que 3.8, vous obtiendrez probablement des résultats différents.

Pour vérifier quelle version de Python vous utilisez, entrez ce qui suit dans une fenêtre de terminal (la mienne renvoie 3.8).

$ python – version
Python 3.8.0

Voici un exemple minimal qui montre comment utiliser un dictionnaire dans un shell Python interactif. N'hésitez pas à jouer !

Exercice :Ajoutez 2 pommes et 3 oranges à votre panier de fruits ! Combien y a-t-il de fruits dans votre panier ?

Tutoriel vidéo sur le dictionnaire Python

Vous ne voulez pas lire l'article ? Pas de problème, regardez-moi parcourir l'article :

Voici le lien vers le cours de freelance Python au cas où vous voudriez commencer à être votre propre patron avec Python.

Dictionnaire Python :pourquoi est-il si utile ?

Quand j'ai découvert les dictionnaires pour la première fois, je ne savais pas s'ils allaient être très utiles. Ils semblaient un peu maladroits et j'avais l'impression que les listes seraient beaucoup plus utiles. Mais j'avais tort !

Dans la vraie vie, un dictionnaire est un livre rempli de mots classés par ordre alphabétique. À côté de chaque mot se trouve une définition. S'il a plusieurs significations, il existe plusieurs définitions. Chaque mot apparaît exactement une fois.

  • Un livre de mots classés par ordre alphabétique.
  • Chaque mot a une définition associée
  • Si un mot a plusieurs significations, il a plusieurs définitions
  • Au fur et à mesure que le temps change, d'autres significations peuvent être ajoutées à un mot.
  • L'orthographe d'un mot ne change jamais.
  • Chaque mot apparaît exactement une fois.
  • Certains mots ont la même définition.

Si nous faisons abstraction de cette idée, nous pouvons voir un dictionnaire comme une correspondance entre un mot et sa définition. Pour rendre cela plus abstrait, un dictionnaire est une correspondance entre quelque chose que nous connaissons (un mot) et quelque chose que nous ne connaissons pas (sa définition).

Nous appliquons ce mappage tout le temps dans la vraie vie : dans notre téléphone, nous mappons les noms de nos amis à leurs numéros de téléphone.

Dans notre esprit, nous associons le nom d'une personne à son visage.

Nous associons les mots à leur sens.

Cette « cartographie » est vraiment facile à comprendre pour les humains et rend la vie beaucoup plus efficace. On le fait tout le temps sans même s'en rendre compte. Il est donc logique que Python l'inclue en tant que type de données fondamental.

Structure du dictionnaire Python

Un dictionnaire traditionnel fait correspondre les mots aux définitions. Les dictionnaires Python peuvent contenir n'importe quel type de données, nous disons donc qu'ils mappent les clés aux valeurs. Chacun est appelé une paire clé-valeur.

La touche "déverrouille" la valeur. Une clé doit être facile à retenir et ne pas changer avec le temps. La valeur peut être plus compliquée et peut changer au fil du temps.

Nous allons maintenant exprimer la même liste que ci-dessus en utilisant la terminologie du dictionnaire Python.

  • Le dictionnaire Python est une collection d'objets (clés et valeurs)
  • Chaque clé a une valeur associée
  • Une clé peut avoir plusieurs valeurs
  • Au fur et à mesure que le temps change, d'autres valeurs peuvent être ajoutées à une clé (les valeurs sont modifiables)
  • Une clé ne peut pas changer (les clés sont immuables)
  • Chaque clé apparaît exactement une fois
  • Les clés peuvent avoir la même valeur

Remarque :on peut commander des dictionnaires si on veut mais ce n'est pas nécessaire de le faire. Nous expliquerons tous ces concepts plus en détail tout au long de l'article. Mais avant de faire quoi que ce soit, il faut savoir créer un dictionnaire !

Python Créer un dictionnaire

Il existe deux manières de créer un dictionnaire en Python :

  1. Accolades { }
  2. Le dict() constructeur

Accolades { }

my_dict = {key1: value1,
           key2: value2,
           key3: value3,
           key4: value4,
           key5: value5}

Nous écrivons la clé, immédiatement suivie de deux-points. Puis un seul espace, la valeur et enfin une virgule. Après la dernière paire, remplacez la virgule par une accolade fermante.

Vous pouvez écrire toutes les paires sur la même ligne. Je mets chacun sur une ligne distincte pour faciliter la lisibilité.

Disons que vous avez 5 amis et que vous souhaitez enregistrer de quel pays ils viennent. Vous l'écririez ainsi (les noms et les pays commencent par la même lettre pour qu'ils soient faciles à retenir !).

names_and_countries = {'Adam': 'Argentina',
                       'Beth': 'Bulgaria',
                       'Charlie': 'Colombia',
                       'Dani': 'Denmark',
                       'Ethan': 'Estonia'}

Le constructeur dict()

Option 1 - plus rapide à taper

my_dict = dict(key1=value1,
               key2=value2,
               key3=value3,
               key4=value4,
               key5=value5)

Ainsi, names_and_countries devient

names_and_countries = dict(Adam='Argentina',
                           Beth='Bulgaria',
                           Charlie='Colombia',
                           Dani='Denmark',
                           Ethan='Estonia')

Chaque paire est comme un argument mot-clé dans une fonction. Les clés sont automatiquement converties en chaînes, mais les valeurs doivent être saisies en tant que chaînes.

Option 2 :plus lente à taper, mieux utilisée avec zip()

my_dict = dict([(key1, value1),
                (key2, value2),
                (key3, value3),
                (key4, value4),
                (key5, value5)])

names_and_countries devient

names_and_countries = dict([('Adam', 'Argentina'),
                            ('Beth', 'Bulgaria'),
                            ('Charlie', 'Colombia'),
                            ('Dani', 'Denmark'),
                            ('Ethan', 'Estonia')])

Comme avec les accolades, nous devons taper explicitement les chaînes en tant que chaînes. Si vous oubliez les guillemets, Python l'interprète comme une fonction.

Option 2 avec zip() - Liste Python à dicter

Si vous avez deux listes et que vous voulez en faire un dictionnaire, faites ceci

names = ['Adam', 'Beth', 'Charlie', 'Dani', 'Ethan']
countries = ['Argentina', 'Bulgaria', 'Colombia', 'Denmark', 'Estonia']
 
# Keys are names, values are countries
names_and_countries = dict(zip(names, countries))
 
>>> names_and_countries
{'Adam': 'Argentina',
'Beth': 'Bulgaria',
'Charlie': 'Colombia',
'Dani': 'Denmark',
'Ethan': 'Estonia'}

Si vous avez plus de deux listes, faites ceci

names = ['Adam', 'Beth', 'Charlie', 'Dani', 'Ethan']
countries = ['Argentina', 'Bulgaria', 'Colombia', 'Denmark', 'Estonia']
ages = [11, 24, 37, 75, 99]
 
# Zip all values together
values = zip(countries, ages)
 
# Keys are names, values are the tuple (countries, ages)
people_info = dict(zip(names, values))
 
>>> people_info
{'Adam': ('Argentina', 11),
'Beth': ('Bulgaria', 24),
'Charlie': ('Colombia', 37),
'Dani': ('Denmark', 75),
'Ethan': ('Estonia', 99)}

C'est la première fois que nous voyons un dictionnaire contenant plus que de simples chaînes ! Nous découvrirons bientôt ce qui peut et ne peut pas être une clé ou une valeur. Mais d'abord, voyons comment accéder à nos données.

Accéder aux paires clé-valeur

Il existe 2 façons d'accéder aux données de nos dictionnaires :

  • Notation entre crochets [ ]
  • La méthode get()

Notation entre parenthèses [ ]

# Get value for the key 'Adam'
>>> names_and_countries['Adam']
'Argentina'
 
# Get value for the key 'Charlie'
>>> names_and_countries['Charlie']
'Colombia'
 
# KeyError if you search for a key not in the dictionary
>>> names_and_countries['Zoe']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'Zoe'

Tapez la clé entre crochets pour obtenir la valeur correspondante. Si vous entrez une clé qui n'est pas dans le dictionnaire, Python lève un KeyError .

Cela ressemble à une indexation de liste mais c'est complètement différent ! Par exemple, vous ne pouvez pas accéder aux valeurs par leur position relative ou par découpage.

# Not the first element of the dictionary
>>> names_and_countries[0]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 0
 
# Not the last element
>>> names_and_countries[-1]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: -1
 
# You cannot slice
>>> names_and_countries['Adam':'Dani']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'slice'

Python s'attend à ce que tout ce qui se trouve entre les crochets soit une clé. Donc pour les deux premiers exemples, nous avons un KeyError car ni 0 ni -1 ne sont des clés dans le dictionnaire. Mais il est possible d'utiliser 0 ou -1 comme clé, comme nous le verrons bientôt.

Remarque :depuis Python 3.7, l'ordre dans lequel les éléments sont ajoutés est conservé. Cependant, vous ne pouvez pas utiliser cette commande pour accéder aux éléments. C'est plus pour l'itération et à des fins visuelles comme nous le verrons plus tard.

Si nous essayons de découper notre dictionnaire, Python lève un TypeError . Nous expliquons pourquoi dans la section Hachage.

Examinons la deuxième méthode pour accéder aux données stockées dans notre dictionnaire.

Méthode get() du dictionnaire Python

# Get value for the key 'Adam'
>>> names_and_countries.get('Adam')
'Argentina'
 
# Returns None if key not in the dictionary
>>> names_and_countries.get('Zoe')
 
# Second argument returned if key not in dictionary
>>> names_and_countries.get('Zoe', 'Name not in dictionary')
'Name not in dictionary'
 
# Returns value if key in dictionary
>>> names_and_countries.get('Charlie', 'Name not in dictionary')
'Colombia'

Le get() la méthode prend deux arguments :

  1. La clé que vous souhaitez rechercher
  2. (facultatif) Valeur à renvoyer si la clé n'est pas dans le dictionnaire (la valeur par défaut est None).

Cela fonctionne comme la notation entre parenthèses. Mais cela ne soulèvera jamais un KeyError . Au lieu de cela, il renvoie soit None, soit l'objet que vous avez entré comme deuxième argument.

Ceci est extrêmement bénéfique si vous parcourez un dictionnaire. Si vous utilisez la notation entre parenthèses et rencontrez une erreur, toute l'itération s'arrêtera. Si vous utilisez get(), aucune erreur ne sera générée et l'itération se terminera.

Nous verrons bientôt comment parcourir les dictionnaires. Mais ça ne sert à rien de faire ça si on ne sait même pas ce que peut contenir notre dictionnaire ! Découvrons ce qui peut et ne peut pas être une paire clé-valeur.

Clés Python Dict

Dans les vrais dictionnaires, l'orthographe des mots ne change pas. Il serait assez difficile d'en utiliser un s'ils le faisaient. Il en va de même pour les dictionnaires Python. Les clés ne peuvent pas changer. Mais ils peuvent être plus que de simples chaînes. En fait, les clés peuvent être n'importe quel type de données immuable :string, int, float, bool ou tuple.

>>> string_dict = {'hi': 'hello'}
>>> int_dict = {1: 'hello'}
>>> float_dict = {1.0: 'hello'}
>>> bool_dict = {True: 'hello', False: 'goodbye'}
>>> tuple_dict = {(1, 2): 'hello'}
 
# Tuples must only contain immutable types
>>> bad_tuple_dict = {(1, [2, 3]): 'hello'}
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

C'est la deuxième fois que nous voyons "TypeError: unhashable type: 'list'" . Alors, que signifie "non hachable" ?

Qu'est-ce que le hachage en Python ?

En arrière-plan, un dictionnaire Python est une structure de données connue sous le nom de table de hachage. Il contient des clés et des valeurs de hachage (nombres de longueur fixe). Vous appliquez hash() à une clé pour renvoyer sa valeur de hachage. Si nous appelons hash() sur la même touche plusieurs fois, le résultat ne changera pas.

# Python 3.8 (different versions may give different results)
>>> hash('hello world!')
1357213595066920515
 
# Same result as above
>>> hash('hello world!')
1357213595066920515
 
# If we change the object, we change the hash value
>>> hash('goodbye world!')
-7802652278768862152

Lorsque nous créons une paire clé-valeur, Python crée une paire hachage-valeur en arrière-plan

# We write
>>> {'hello world!': 1}
 
# Python executes in the background
>>> {hash('hello world!'): 1}
 
# This is equivalent to
>>> {1357213595066920515: 1}

Python utilise cette valeur de hachage lorsque nous recherchons une paire clé-valeur. De par sa conception, la fonction de hachage ne peut être appliquée qu'aux types de données immuables. Si les clés pouvaient changer, Python devrait créer une nouvelle table de hachage à chaque fois que vous les modifiez. Cela entraînerait d'énormes inefficacités et de nombreux bogues.

Au lieu de cela, une fois qu'une table est créée, la valeur de hachage ne peut pas changer. Python sait quelles valeurs se trouvent dans la table et n'a pas besoin de les recalculer. Cela rend les opérations de recherche dans le dictionnaire et d'appartenance instantanées et de O (1).

En Python, le concept de hachage n'apparaît que lorsque l'on parle de dictionnaires. Alors que les types de données mutables et immuables apparaissent partout. Ainsi, nous disons que vous ne pouvez utiliser que des types de données immuables comme clés, plutôt que de dire des types de données « hachables ».

Enfin, que se passe-t-il si vous utilisez la valeur de hachage d'un objet comme une autre clé dans le même dictionnaire ? Python devient-il confus ?

>>> does_this_work = {'hello': 1,
   			   hash('hello'): 2}
 
>>> does_this_work['hello']
1
 
>>> does_this_work[hash('hello')]
2

Ça marche! Les raisons pour lesquelles sont au-delà de la portée de cet article. La mise en œuvre complète de l'algorithme et les raisons pour lesquelles il fonctionne sont décrites ici. Tout ce que vous devez vraiment savoir, c'est que Python sélectionne toujours la bonne valeur… même si vous essayez de la confondre !

Valeurs du dictionnaire Python

Il existe des restrictions sur les clés de dictionnaire mais les valeurs n'en ont pas. Littéralement, tout peut être une valeur. Tant que votre clé est un type de données immuable, vos paires clé-valeur peuvent être n'importe quelle combinaison de types que vous souhaitez. Vous avez le contrôle total !

>>> crazy_dict = {11.0: ('foo', 'bar'),
                  'baz': {1: 'a', 2: 'b'},
                  (42, 55): {10, 20, 30},
                  True: False}
 
# Value of the float 11.0 is a tuple
>>> crazy_dict[11.0]
('foo', 'bar')
 
# Value of the string 'baz' is a dictionary
>>> crazy_dict.get('baz')
{1: 'a', 2: 'b'}
 
# Value of the tuple (42, 55) is a set
>>> crazy_dict[(42, 55)]
{10, 20, 30}
 
# Value of the Bool True is the Bool False
>>> crazy_dict.get(True)
False

Remarque :vous devez utiliser la notation par accolades pour taper un dictionnaire comme celui-ci. Si vous essayez d'utiliser le dict() constructeur, vous obtiendrez des erreurs de syntaxe (sauf si vous utilisez la méthode détaillée et tapez une liste de tuples… mais pourquoi feriez-vous cela ?).

Si vous avez besoin de rafraîchir vos connaissances de base sur les ensembles Python, je vous recommande de lire le guide ultime des ensembles Python sur le blog Finxter.

Dictionnaires imbriqués Python

Lors du web scraping, il est très courant de travailler avec des dictionnaires à l'intérieur de dictionnaires (dictionnaires imbriqués). Pour accéder aux valeurs à des niveaux plus profonds, il vous suffit d'enchaîner les méthodes. N'importe quel ordre de notation entre parenthèses et get() est possible.

# Returns a dict
>>> crazy_dict.get('baz')
{1: 'a', 2: 'b'}
 
# Chain another method to access the values of this dict
>>> crazy_dict.get('baz').get(1)
'a'
 
>>> crazy_dict.get('baz')[2]
'b'

Nous savons maintenant comment créer un dictionnaire et quels types de données sont autorisés où. Mais que se passe-t-il si vous avez déjà créé un dictionnaire et souhaitez y ajouter plus de valeurs ?

Python Ajouter au dictionnaire

>>> names_and_countries
{'Adam': 'Argentina', 
'Beth': 'Bulgaria', 
'Charlie': 'Colombia', 
'Dani': 'Denmark', 
'Ethan': 'Estonia'}
 
# Add key-value pair 'Zoe': 'Zimbabwe'
>>> names_and_countries['Zoe'] = 'Zimbabwe'
 
# Add key-value pair 'Fred': 'France'
>>> names_and_countries['Fred'] = 'France'
 
# Print updated dict
>>> names_and_countries
{'Adam': 'Argentina', 
'Beth': 'Bulgaria', 
'Charlie': 'Colombia', 
'Dani': 'Denmark', 
'Ethan': 'Estonia', 
'Zoe': 'Zimbabwe',     # Zoe first
'Fred': 'France'}      # Fred afterwards

Notre dictionnaire reflète l'ordre dans lequel nous avons ajouté les paires en montrant d'abord Zoé puis Fred.

Pour ajouter une nouvelle paire clé-valeur, nous supposons simplement que la clé existe déjà et essayons d'y accéder via la notation entre parenthèses

>>> my_dict['new_key']

Ensuite (avant d'appuyer sur Entrée), utilisez l'opérateur d'affectation "=" et indiquez une valeur.

>>> my_dict['new_key'] = 'new_value'

Vous ne pouvez pas attribuer de nouvelles paires clé-valeur via le get() méthode car il s'agit d'un appel de fonction.

>>> names_and_countries.get('Holly') = 'Hungary'
File "<stdin>", line 1
SyntaxError: cannot assign to function call

Pour supprimer une paire clé-valeur, utilisez le del déclaration. Pour modifier la valeur d'une clé existante, utilisez la même notation entre parenthèses que ci-dessus.

# Delete the Zoe entry
>>> del names_and_countries['Zoe']
 
# Change Ethan's value
>>> names_and_countries['Ethan'] = 'DIFFERENT_COUNTRY'
 
>>> names_and_countries
{'Adam': 'Argentina', 
'Beth': 'Bulgaria', 
'Charlie': 'Colombia', 
'Dani': 'Denmark', 
'Ethan': 'DIFFERENT_COUNTRY',  # Ethan has changed
'Fred': 'France'}    		  # We no longer have Zoe

Comme pour les autres types de données modifiables, soyez prudent lorsque vous utilisez le del déclaration dans une boucle. Il modifie le dictionnaire en place et peut entraîner des conséquences imprévues. La meilleure pratique consiste à créer une copie du dictionnaire et à modifier la copie. Ou vous pouvez utiliser, mon préféré, les compréhensions de dictionnaire (que nous aborderons plus tard) - une fonctionnalité puissante similaire à la fonctionnalité populaire de compréhension de liste en Python.

Méthode de copie Python Dict

>>> my_dict = {'a': 1, 'b': 2}
 
# Create a shallow copy
>>> shallow_copy = my_dict.copy()
 
# Create a deep copy
>>> import copy
>>> deep_copy = copy.deepcopy(my_dict)

Pour créer une copie superficielle d'un dictionnaire, utilisez le copy() méthode. Pour créer une copie en profondeur, utilisez le deepcopy() méthode du module de copie intégré. Nous ne discuterons pas de la distinction entre les méthodes de copie dans cet article par souci de brièveté.

Vérification de l'appartenance au dictionnaire

Disons que nous avons un dictionnaire avec 100 000 paires clé-valeur. Nous ne pouvons pas l'imprimer à l'écran et vérifier visuellement les paires clé-valeur qu'il contient.

Heureusement, la syntaxe suivante est la même pour les dictionnaires que pour les autres objets tels que les listes et les ensembles. Nous utilisons le in mot-clé.

# Name obviously not in our dict
>>> 'INCORRECT_NAME' in names_and_countries
False
 
# We know this is in our dict
>>> 'Adam' in names_and_countries
True
 
# Adam's value is in the dict... right?
>>> names_and_countries['Adam']
'Argentina'
>>> 'Argentina' in names_and_countries
False

Nous nous attendons à ce que INCORRECT_NAME ne soit pas dans notre dict et qu'Adam y soit. Mais pourquoi « Argentine » renvoie-t-il Faux ? On vient de voir que c'est la valeur d'Adam ?!

Vous vous souvenez au début de l'article que j'ai dit que les dictionnaires sont des cartes ? Ils correspondent à quelque chose que nous connaissons (la clé) à quelque chose que nous ne connaissons pas (la valeur). Ainsi, lorsque nous demandons si quelque chose se trouve dans notre dictionnaire, nous demandons s'il s'agit d'une clé. Nous ne demandons pas si c'est une valeur.

Ce qui est plus naturel lorsqu'on pense à un dictionnaire réel :

  1. Le mot "facétieux" est-il dans ce dictionnaire ?
  2. Le mot signifie-t-il "sans intention sérieuse ?" préoccupé par quelque chose de non essentiel, d'amusant ou de frivole » dans ce dictionnaire ?

Il est clair que le premier est le gagnant et c'est le comportement par défaut de Python.

>>> 'something' in my_dict

Nous vérifions si "quelque chose" est une clé dans my_dict.

Mais n'ayez crainte, si vous voulez vérifier si une valeur spécifique se trouve dans un dictionnaire, c'est possible ! Nous devons simplement utiliser certaines méthodes.

Méthodes du dictionnaire Python – Clés, valeurs et éléments

Il y a 3 méthodes à regarder. Tout peut être utilisé pour vérifier l'appartenance ou pour parcourir des parties spécifiques d'un dictionnaire. Chacun renvoie un itérable.

  • .keys() - itère sur les clés du dictionnaire
  • .values() - itérer sur les valeurs du dictionnaire
  • .items() - itérer sur les clés et les valeurs du dictionnaire

Remarque :nous avons remplacé le pays d'Ethan par l'Estonie pour plus de lisibilité.

>>> names_and_countries.keys()
dict_keys(['Adam', 'Beth', 'Charlie', 'Dani', 'Ethan', 'Fred'])
 
>>> names_and_countries.values()
dict_values(['Argentina', 'Bulgaria', 'Colombia', 'Denmark', 'Estonia', 'France'])
 
>>> names_and_countries.items()
 
 
dict_items([('Adam', 'Argentina'), 
            ('Beth', 'Bulgaria'), 
            ('Charlie', 'Colombia'), 
            ('Dani', 'Denmark'), 
            ('Ethan', 'Estonia'), 
            ('Fred', 'France')])

Nous pouvons maintenant vérifier l'appartenance aux clés et aux valeurs :

# Check membership in dict's keys
>>> 'Adam' in names_and_countries
True
>>> 'Adam' in names_and_countries.keys()
True
 
# Check membership in the dict's values
>>> 'Argentina' in names_and_countries.values()
True
 
# Check membership in either keys or values???
>>> 'Denmark' in names_and_countries.items()
False

Vous ne pouvez pas archiver les clés et les valeurs en même temps. C'est parce que items() renvoie un itérable de tuples. Comme 'Denmark' n'est pas un tuple, il renverra False.

>>> for thing in names_and_countries.items():
  	  print(thing)
('Adam', 'Argentina')
('Beth', 'Bulgaria')
('Charlie', 'Colombia')
('Dani', 'Denmark')
('Ethan', 'Estonia')
 
# True because it's a tuple containing a key-value pair
>>> ('Dani', 'Denmark') in names_and_countries.items()
True

Python Loop Through Dictionary – Aperçu

Pour parcourir n'importe quelle partie du dictionnaire, nous pouvons utiliser une boucle for

>>> for name in names_and_countries.keys():
        print(name)
Adam
Beth
Charlie
Dani
Ethan
Fred
 
>>> for country in names_and_countries.values():
        print(f'{country} is wonderful!')
Argentina is wonderful!
Bulgaria is wonderful!
Colombia is wonderful!
Denmark is wonderful!
Estonia is wonderful!
France is wonderful!
 
>>> for name, country in names_and_countries.items():
        print(f'{name} is from {country}.')
Adam is from Argentina.
Beth is from Bulgaria.
Charlie is from Colombia.
Dani is from Denmark.
Ethan is from Estonia.
Fred is from France.

Il est recommandé d'utiliser des noms descriptifs pour les objets sur lesquels vous parcourez. Le code est fait pour être lu et compris par les humains ! Ainsi, nous avons choisi « nom » et « pays » plutôt que « clé » et « valeur ».

# Best practice
>>> for descriptive_key, descriptive_value in my_dict.items():
        # do something
 
# Bad practice (but you will see it 'in the wild'!)
>>> for key, value in my_dict.items():
        # do something

Si vos paires clé-valeur ne suivent pas un modèle spécifique, vous pouvez utiliser « clé » et « valeur » comme variables itérables, ou même « k » et « v ».

# Iterating over the dict is the same as dict.keys()
>>> for thing in names_and_countries:
        print(thing)
Adam
Beth
Charlie
Dani
Ethan
Fred

Une note sur la réutilisation

# Works with general Python types
>>> for key in object:
        # do something
 
# Works only with dictionaries
>>> for key in object.keys():
        # do something

Ne spécifiez pas keys() si votre code doit fonctionner avec d'autres objets comme des listes et des ensembles. Utilisez la méthode keys() si votre code est uniquement destiné aux dictionnaires. Cela empêche les futurs utilisateurs de saisir des objets incorrects.

Python dict has_key

La méthode has_key() est exclusive à Python 2. Elle renvoie True si la clé est dans le dictionnaire et False sinon.

Python 3 a supprimé cette fonctionnalité au profit de la syntaxe suivante :

>>> if key in d:
        # do something

Cela maintient la syntaxe du dictionnaire en ligne avec celle des autres types de données tels que les ensembles et les listes. Cela facilite la lisibilité et la réutilisation.

De jolis dictionnaires d'impression avec pprint()

Le module intégré pprint contient la fonction pprint. Cela "imprimera joliment" votre dictionnaire. Il trie les clés par ordre alphabétique et imprime chaque paire clé-valeur sur une nouvelle ligne.

>>> from pprint import pprint
>>> messy_dict = dict(z='Here is a really long key that spans a lot of text', a='here is another long key that is really too long', j='this is the final key in this dictionary')
 
>>> pprint(messy_dict)
{'a': 'here is another long key that is really too long',
'j': 'this is the final key in this dictionary',
'z': 'Here is a really long key that spans a lot of text'}

Cela ne change en rien le dictionnaire. C'est juste beaucoup plus lisible maintenant.

Dictionnaires Python et fichiers JSON

Nous devons encoder et décoder toutes ces données .

Un type de fichier courant avec lequel vous interagirez est un fichier JSON. Il signifie Javascript Object Notation. Ils sont utilisés pour structurer et envoyer des données dans des applications Web.

Ils fonctionnent presque exactement de la même manière que les dictionnaires et vous pouvez facilement transformer l'un en l'autre très facilement.

Python Dict vers JSON

>>> import json
>>> my_dict = dict(a=1, b=2, c=3, d=4)
 
>>> with open('my_json.json', 'w') as f:
   	 json.dump(my_dict, f)

Le code ci-dessus prend my_dict et l'écrit dans le fichier my_json.json dans le répertoire courant.

Vous pouvez devenir plus complexe que cela en définissant des encodages de caractères et des espaces. Pour plus de détails, nous renvoyons le lecteur aux docs.

Python JSON vers Dict

Nous avons le fichier my_json.json dans notre répertoire de travail actuel.

>>> import json
>>> with open('my_json.json', 'r') as f:
        new_dict = json.load(f)
 
>>> new_dict
{'a': 1, 'b': 2, 'c': 3, 'd': 4}

Remarque :les paires clé-valeur dans JSON sont toujours converties en chaînes lorsqu'elles sont encodées en Python. Il est facile de transformer n'importe quel objet en chaîne et cela entraîne moins d'erreurs lors de l'encodage et du décodage des fichiers. Mais cela signifie que parfois le fichier que vous chargez et le fichier avec lequel vous avez commencé ne sont pas identiques.

Méthodes du dictionnaire Python

Voici un bref aperçu :

  1. dict.clear() - supprime toutes les paires clé-valeur d'un dict
  2. dict.update() – fusionner deux dictionnaires ensemble
  3. dict.pop() – supprime une clé et renvoie sa valeur
  4. dict.popitem() - supprime une paire clé-valeur aléatoire et la renvoie sous forme de tuple

Nous utiliserons les lettres A et B pour nos dictionnaires car elles sont plus faciles à lire que les noms descriptifs. De plus, nous avons gardé les exemples simples pour faciliter la compréhension.

dict.clear() - supprime toutes les paires clé-valeur d'un dict

>>> A = dict(a=1, b=2)
>>> A.clear()
>>> A
{}

Appeler ceci sur un dict supprime toutes les paires clé-valeur en place. Le dict est maintenant vide.

dict.update() – fusionner deux dictionnaires ensemble

>>> A = dict(a=1, b=2)
>>> B = dict(c=3, d=4)
>>> A.update(B)
>>> A
{'a': 1, 'b': 2, 'c': 3, 'd': 4}
>>> B
{'c': 3, 'd': 4}

Nous venons de mettre à jour A. Ainsi, toutes les paires clé-valeur de B ont été ajoutées à A. B n'a pas changé.

Si A et B certaines touches, la valeur de B remplacera celle de A. En effet, A est mis à jour par B et prend donc toutes les valeurs de B (et non l'inverse).

>>> A = dict(a=1, b=2)
>>> B = dict(b=100)
>>> A.update(B)
 
# A now contains B's values
>>> A
{'a': 1, 'b': 100}
 
# B is unchanged
>>> B
{'b': 100}

Vous pouvez également passer une séquence de tuples ou d'arguments de mots-clés à update(), comme vous le feriez avec le constructeur dict().

>>> A = dict(a=1, b=2)
# Sequence of tuples
>>> B = [('c', 3), ('d', 4)]
>>> A.update(B)
>>> A
{'a': 1, 'b': 2, 'c': 3, 'd': 4}
 
>>> A = dict(a=1, b=2)
# Pass key-value pairs as keyword arguments
>>> A.update(c=3, d=4)
>>> A
{'a': 1, 'b': 2, 'c': 3, 'd': 4}

dict.pop() – supprime une clé et renvoie sa valeur

>>> A = dict(a=1, b=2)
>>> A.pop('a')
1
>>> A
{'b': 2}

Si vous essayez d'appeler dict.pop() avec une clé qui n'est pas dans le dictionnaire, Python lève une KeyError.

>>> A.pop('non_existent_key')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'non_existent_key'

Comme la méthode get(), vous pouvez spécifier un deuxième argument facultatif. Ceci est renvoyé si la clé n'est pas dans le dictionnaire et évite ainsi les KeyErrors.

>>> A.pop('non_existent_key', 'not here')
'not here'

dict.popitem() - supprime une paire clé-valeur aléatoire et la renvoie sous forme de tuple

>>> A = dict(a=1, b=2, c=3)
# Your results will probably differ
>>> A.popitem()
('c', 3)
>>> A
{'a': 1, 'b': 2}
>>> A.popitem()
('b', 2)
>>> A
{'a': 1}

Si le dictionnaire est vide, Python lève une KeyError.

>>> A = dict()
>>> A.popitem()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'popitem(): dictionary is empty'

Python Loop Through Dictionary - En détail

Il existe plusieurs situations courantes que vous rencontrerez lors de l'itération sur des dictionnaires. Python a développé plusieurs méthodes pour vous aider à travailler plus efficacement.

Mais avant d'aller plus loin, rappelez-vous ce qui suit :

N'utilisez JAMAIS JAMAIS la notation entre parenthèses lors d'une itération sur un dictionnaire. S'il y a des erreurs, toute l'itération se cassera et vous ne serez pas content.

La notation Python standard pour incrémenter des nombres ou ajouter à des listes est

# Counting
my_num = 0
for thing in other_thing:
    my_num += 1
 
# Appending to lists
my_list = []
for thing in other_thing:
    my_list.append(thing)

Cela suit le modèle standard :

  1. Initialiser l'objet "vide"
  2. Commencer la boucle
  3. Ajouter des éléments à cet objet

Lors de l'itération sur un dictionnaire, nos valeurs peuvent être des nombres ou des listes. Ainsi, nous pouvons ajouter ou ajouter des valeurs. Ce serait formidable si notre code suivait le modèle ci-dessus. Mais…

>>> my_dict = {}
>>> for thing in other_thing:
        my_dict['numerical_key'] += 1
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
KeyError: 'numerical_key'
 
>>> for thing in other_thing:
        my_dict['list_key'].append(thing)
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
KeyError: 'list_key'

Malheureusement, les deux génèrent une KeyError. Python nous dit que la clé n'existe pas et nous ne pouvons donc pas incrémenter sa valeur. Ainsi, nous devons d'abord créer une paire clé-valeur avant de faire quoi que ce soit avec.

Nous allons maintenant montrer 4 façons de résoudre ce problème :

  1. Initialiser manuellement une clé si elle n'existe pas
  2. La méthode get()
  3. La méthode setdefault()
  4. Le defaultdict()

Nous allons expliquer cela à travers quelques exemples, alors passons à la configuration.

Trois amis - Adam, Bella et Cara, sont sortis pour un repas le jour de l'anniversaire d'Adam. Ils ont stocké leurs commandes d'entrées, de plats principaux et de boissons dans une seule liste. Le prix de chaque article est dans une autre liste. Nous utiliserons ces données pour construire différents dictionnaires.

people = ['Adam', 'Bella', 'Cara',
          'Adam', 'Bella', 'Cara',
          'Adam', 'Bella', 'Cara',]
 
food = ['soup', 'bruschetta', 'calamari',   # starter
        'burger', 'calzone', 'pizza',       # main
        'coca-cola', 'fanta', 'water']      # drink
 
# Cost of each item in £
prices = [3.20, 4.50, 3.89,
          12.50, 15.00, 13.15,
          3.10, 2.95, 1.86]
 
# Zip data together to allow iteration
# We only need info about the person and the price
meal_data = zip(people, prices)

Nos trois amis sont très stricts avec leur argent. Ils veulent payer exactement le montant qu'ils ont commandé. Nous allons donc créer un dictionnaire contenant le coût total pour chaque personne. C'est un problème d'incrémentation numérique.

Initialiser manuellement une clé

# Initialise empty dict
total = {}
 
# Iterate using descriptive object names
for (person, price) in meal_data:
 
    # Create new key and set value to 0 if key doesn't yet exist
    if person not in total:
        total[person] = 0
    
    # Increment the value by the price of each item purchased.
    total[person] += price
 
>>> total
{'Adam': 18.8, 'Bella': 22.45, 'Cara': 18.9}

Nous écrivons une instruction if qui vérifie si la clé est déjà dans le dictionnaire. Si ce n'est pas le cas, nous définissons la valeur sur 0. Si c'est le cas, Python n'exécute pas l'instruction if. Nous incrémentons ensuite en utilisant la syntaxe attendue.

Cela fonctionne bien mais nécessite pas mal de lignes de code. On peut sûrement faire mieux ?

Méthode Python Dict get() lors de l'itération

# Reinitialise meal_data as we have already iterated over it
meal_data = zip(people, prices)
 
total = {}
for (person, price) in meal_data:
 
    # get method returns 0 the first time we call it
    # and returns the current value subsequent times
    total[person] = total.get(person, 0) + price
 
>>> total
{'Adam': 18.8, 'Bella': 22.45, 'Cara': 18.9}

Nous l'avons réduit à une seule ligne !

Nous passons à get() une deuxième valeur qui est renvoyée si la clé n'est pas dans le dictionnaire. Dans ce cas, nous choisissons 0 comme dans l'exemple ci-dessus. La première fois que nous appelons get(), elle renvoie 0. Nous venons d'initialiser une paire clé-valeur ! Dans la même ligne, nous ajoutons "prix". La prochaine fois que nous appellerons get(), il retournera la valeur actuelle et nous pourrons à nouveau ajouter ‘price’.

Cette méthode ne fonctionne pas pour l'ajout. Vous avez besoin de quelques lignes de code supplémentaires. Nous allons plutôt nous intéresser à la méthode setdefault().

Méthode Python Dict setdefault()

La syntaxe de cette méthode en fait un excellent choix pour modifier la valeur d'une clé via le append() méthode.

Nous allons d'abord montrer pourquoi ce n'est pas un bon choix à utiliser si vous incrémentez avec des nombres.

meal_data = zip(people, prices)
total = {}
for (person, price) in meal_data:
 
    # Set the initial value of person to 0
    total.setdefault(person, 0)
 
    # Increment by price
    total[person] += price
 
0
0
0
3.2
4.5
3.89
15.7
19.5
17.04
>>> total
{'Adam': 18.8, 'Bella': 22.45, 'Cara': 18.9}

Cela fonctionne mais nécessite plus de lignes de code que get() et imprime beaucoup de nombres à l'écran. Pourquoi est-ce ?

La méthode setdefault() prend deux arguments :

  1. La clé pour laquelle vous souhaitez définir une valeur par défaut
  2. Ce que vous voulez que la valeur par défaut soit

Ainsi, setdefault(person, 0) définit la valeur par défaut de person sur 0.

Il renvoie toujours l'une des deux choses :

  1. La valeur actuelle de la clé
  2. Si la clé n'existe pas, elle renvoie la valeur par défaut fournie

C'est pourquoi les chiffres sont imprimés à l'écran. Ce sont les valeurs de « personne » à chaque itération.

De toute évidence, ce n'est pas la méthode la plus pratique pour notre problème actuel. Si nous faisons 100 000 itérations, nous ne voulons pas que 100 000 nombres soient imprimés à l'écran.

Nous vous recommandons donc d'utiliser la méthode get() pour les calculs numériques.

Voyons-le en action avec des listes et des ensembles. Dans ce dictionnaire, le nom de chaque personne est une clé. Chaque valeur est une liste contenant le prix de chaque article commandé (entrée, plat, dessert).

meal_data = zip(people, prices)
individual_bill = {}
 
for (person, price) in meal_data:
 
    # Set default to empty list and append in one line!
    individual_bill.setdefault(person, []).append(price)
 
>>> individual_bill
{'Adam': [3.2, 12.5, 3.1], 
'Bella': [4.5, 15.0, 2.95], 
'Cara': [3.89, 13.15, 1.86]}

Nous voyons maintenant la vraie puissance de setdefault() ! Comme la méthode get dans notre exemple numérique, nous initialisons une valeur par défaut et la modifions en une ligne !

Remarque :setdefault() calcule la valeur par défaut à chaque appel. Cela peut être un problème si votre valeur par défaut est coûteuse à calculer. Get() ne calcule la valeur par défaut que si la clé n'existe pas. Ainsi, get() est un meilleur choix si votre valeur par défaut est chère. Étant donné que la plupart des valeurs par défaut sont des « zéros », comme 0, [ ] et { }, ce n'est pas un problème dans la plupart des cas.

Nous avons vu trois solutions au problème maintenant. Nous avons le code jusqu'à 1 ligne. Mais la syntaxe pour chacun a été différente de ce que nous voulons. Voyons maintenant quelque chose qui résout le problème exactement comme nous l'espérions :introduire defaultdict !

Python defaultdict()

Résolvons notre problème d'incrémentation numérique :

# Import from collections module
from collections import defaultdict
 
meal_data = zip(people, prices)
 
# Initialise with int to do numerical incrementation
total = defaultdict(int)
 
# Increment exactly as we want to!
for (person, price) in meal_data:
    total[person] += price
 
>>> total
defaultdict(<class 'int'>, {'Adam': 18.8, 'Bella': 22.45, 'Cara': 18.9})

Succès!! Mais qu'en est-il de notre problème de liste ?

from collections import defaultdict
 
meal_data = zip(people, prices)
 
# Initialise with list to let us append
individual_bill = defaultdict(list)
 
for (person, price) in meal_data:
    individual_bill[person].append(price)
 
>>> individual_bill
defaultdict(<class 'list'>, {'Adam': [3.2, 12.5, 3.1], 
                             'Bella': [4.5, 15.0, 2.95], 
                             'Cara': [3.89, 13.15, 1.86]})

Le defaultdict fait partie du module de collections intégré. Donc, avant de l'utiliser, nous devons d'abord l'importer.

Defaultdict est identique à un dictionnaire Python normal, sauf :

  1. Il prend un type de données appelable comme argument
  2. Lorsqu'il rencontre une clé pour la première fois, la valeur par défaut est définie sur "zéro" pour ce type de données. Pour int c'est 0, pour list c'est une liste vide [ ] etc..

Ainsi, vous n'obtiendrez jamais une KeyError! De plus, l'initialisation des valeurs par défaut est prise en charge automatiquement !

Nous avons maintenant résolu le problème en utilisant la même syntaxe pour les listes et les nombres !

Passons maintenant en revue quelques cas particuliers pour defaultdict.

Python defaultdict() Cas particuliers

Ci-dessus, nous avons dit qu'il n'est pas possible d'obtenir une KeyError lors de l'utilisation de defaultdict. Ceci n'est vrai que si vous initialisez correctement votre dict.

# Initialise without an argument
>>> bad_dict = defaultdict()
>>> bad_dict['key']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'key'
 
# Initialise with None
>>> another_bad_dict = defaultdict(None)
>>> another_bad_dict['another_key']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'another_key'

Disons que vous initialisez defaultdict sans aucun argument. Ensuite, Python lève une KeyError si vous appelez une clé qui n'est pas dans le dictionnaire. C'est la même chose que d'initialiser avec None et va à l'encontre de tout l'objectif de defaultdict.

Le problème est que None n'est pas appelable. Pourtant, vous pouvez faire en sorte que defaultdict renvoie None en utilisant une fonction lambda :

>>> none_dict = defaultdict(lambda: None)
>>> none_dict['key']
>>>

Notez que vous ne pouvez pas incrémenter ou ajouter à Aucun. Assurez-vous de choisir votre valeur par défaut pour correspondre au problème que vous résolvez !

Pendant que nous y sommes, jetons un coup d'œil à d'autres dictionnaires dans le module des collections.

DictOrdonné

Nous avons dit précédemment que les dictionnaires conservent leur ordre à partir de Python 3.7. Alors pourquoi avons-nous besoin de quelque chose appelé OrderedDict ?

Comme son nom l'indique, OrderedDict conserve l'ordre dans lequel les éléments sont ajoutés. Mais deux OrderedDicts sont identiques si et seulement si leurs éléments sont dans le même ordre. Ce n'est pas le cas avec les dicts normaux.

>>> from collections import OrderedDict
 
# Normal dicts preserve order but don't use it for comparison
>>> normal1 = dict(a=1, b=2)
>>> normal2 = dict(b=2, a=1)
>>> normal1 == normal2
True
 
# OrderedDicts preserve order and use it for comparison
>>> ordered1 = OrderedDict(a=1, b=2)
>>> ordered2 = OrderedDict(b=2, a=1)
>>> ordered1 == ordered2
False

En dehors de cela, OrderedDict a toutes les mêmes propriétés qu'un dictionnaire ordinaire. Si vos éléments doivent être dans un ordre particulier, utilisez OrderedDict !

Compteur()

Disons que nous voulons compter combien de fois chaque mot apparaît dans un morceau de texte (une chose courante à faire en PNL). Nous utiliserons Le Zen de Python pour notre exemple. Si vous ne savez pas ce que c'est, lancez

>>> import this

Je l'ai stocké dans la liste zen_words où chaque élément est un seul mot.

Nous pouvons compter manuellement chaque mot en utilisant defaultdict. Mais l'imprimer avec les mots les plus fréquents en premier est un peu délicat.

>>> from collections import defaultdict
>>> word_count = defaultdict(int)
>>> for word in zen_words:
        word_count[word] += 1
 
# Define function to return the second value of a tuple
>>> def select_second(tup):
        return tup[1]
 
# Reverse=True - we want the most common first
# word_count.items() - we want keys and values
# sorted() returns a list, so wrap in dict() to return a dict
 
>>> dict(sorted(word_count.items(), reverse=True, key=select_second))
{'is': 10, 
'better': 8, 
'than': 8, 
'to': 5, 
...}

Comme le comptage est un processus assez courant, la sous-classe Counter() dict a été créée. Il est suffisamment complexe pour que nous puissions écrire un article entier à ce sujet.

Par souci de concision, nous inclurons les cas d'utilisation les plus élémentaires et laisserons le lecteur parcourir les documents eux-mêmes.

>>> from collections import Counter
>>> word_count = Counter(zen_words)
>>> word_count
Counter({'is': 10, 'better': 8, 'than': 8, 'to': 5, ...})

Vous pouvez passer n'importe quel itérable ou dictionnaire à Counter(). Il renvoie un dictionnaire dans l'ordre décroissant des comptes

>>> letters = Counter(['a', 'b', 'c', 'c', 'c', 'c', 'd', 'd', 'a'])
>>> letters
Counter({'c': 4, 'a': 2, 'd': 2, 'b': 1})
 
# Count of a missing key is 0
>>> letters['z']
0

Inversé()

Dans Python 3.8, ils ont introduit le reversed() fonction pour les dictionnaires ! Il renvoie un itérateur. Il parcourt le dictionnaire dans l'ordre inverse de la façon dont les paires clé-valeur ont été ajoutées. Si les paires clé-valeur n'ont pas d'ordre, reversed() ne leur donnera aucun ordre supplémentaire. Si vous souhaitez par exemple trier les clés par ordre alphabétique, utilisez sorted() .

# Python 3.8
 
# Reverses the order key-value pairs were added to the dict
>>> ordered_dict = dict(a=1, b=2, c=3)
>>> for key, value in reversed(ordered_dict.items()):
        print(key, value)
c 3
b 2
a 1
 
# Does not insert order where there is none.
>>> unordered_dict = dict(c=3, a=1, b=2)
>>> for key, value in reversed(unordered_dict.items()):
        print(key, value)
b 2
a 1
c 3
 
# Order unordered_dict alphabetically using sorted()
>>> dict(sorted(unordered_dict.items()))
{'a': 1, 'b': 2, 'c': 3}

Puisqu'il s'agit d'un itérateur, n'oubliez pas d'utiliser les méthodes keys(), values() et items() pour sélectionner les éléments souhaités. Si vous ne spécifiez rien, vous parcourrez les clés.

Compréhensions du dictionnaire

Une fonctionnalité merveilleuse des dictionnaires, et de Python en général, est la compréhension. Cela vous permet de créer des dictionnaires de manière propre, facile à comprendre et Pythonique. Vous devez utiliser des accolades {} pour le faire (pas dict()).

Nous avons déjà vu que si vous avez deux listes, vous pouvez en créer un dictionnaire en utilisant dict(zip()).

names = ['Adam', 'Beth', 'Charlie', 'Dani', 'Ethan']
countries = ['Argentina', 'Bulgaria', 'Colombia', 'Denmark', 'Estonia']
 
dict_zip = dict(zip(names, countries))
 
>>> dict_zip
{'Adam': 'Argentina',
'Beth': 'Bulgaria',
'Charlie': 'Colombia',
'Dani': 'Denmark',
'Ethan': 'Estonia'}

Nous pouvons également le faire en utilisant une boucle for

>>> new_dict = {}
>>> for name, country in zip(names, countries):
        new_dict[name] = country
 
>>> new_dict
{'Adam': 'Argentina',
'Beth': 'Bulgaria',
'Charlie': 'Colombia',
'Dani': 'Denmark',
'Ethan': 'Estonia'}

Nous initialisons nos variables dict et iterator avec des noms descriptifs. Pour itérer sur les deux listes en même temps, nous les compressons ensemble. Enfin, nous ajoutons des paires clé-valeur comme souhaité. Cela prend 3 lignes.

L'utilisation d'une compréhension transforme cela en une seule ligne.

dict_comp = {name: country for name, country in zip(names, countries)}
 
>>> dict_comp
{'Adam': 'Argentina',
'Beth': 'Bulgaria',
'Charlie': 'Colombia',
'Dani': 'Denmark',
'Ethan': 'Estonia'}

C'est un peu comme des boucles for à l'envers. Tout d'abord, nous indiquons ce que nous voulons que nos paires clé-valeur soient. Ensuite, nous utilisons la même boucle for que ci-dessus. Enfin, on enveloppe le tout dans des accolades.

Notez que chaque compréhension peut être écrite comme une boucle for. Si jamais vous obtenez des résultats auxquels vous ne vous attendiez pas, essayez-le comme une boucle for pour voir ce qui se passe.

Voici une erreur courante

dict_comp_bad = {name: country 
                 for name in names 
                 for country in countries}
 
>>> dict_comp_bad
{'Adam': 'Estonia',
'Beth': 'Estonia',
'Charlie': 'Estonia',
'Dani': 'Estonia',
'Ethan': 'Estonia'}

Que se passe-t-il? Écrivons-le comme une boucle for pour voir. Tout d'abord, nous allons l'écrire pour nous assurer que nous obtenons le même résultat indésirable.

bad_dict = {}
for name in names:
    for country in countries:
        bad_dict[name] = country
 
>>> bad_dict
{'Adam': 'Estonia',
'Beth': 'Estonia',
'Charlie': 'Estonia',
'Dani': 'Estonia',
'Ethan': 'Estonia'}

Nous allons maintenant utiliser le meilleur ami du chercheur de bogues :la déclaration d'impression !

# Don't initialise dict to just check for loop logic
for name in names:
    for country in countries:
        print(name, country)
Adam Argentina
Adam Bulgaria
Adam Colombia
Adam Denmark
Adam Estonia
Beth Argentina
Beth Bulgaria
Beth Colombia
...
Ethan Colombia
Ethan Denmark
Ethan Estonia

Ici, nous supprimons le dictionnaire pour vérifier ce qui se passe réellement dans la boucle. Maintenant, nous voyons le problème! Le problème est que nous avons des boucles imbriquées. La boucle dit :pour chaque nom, associez-le à chaque pays. Étant donné que les clés de dictionnaire ne peuvent qu'apparaître, la valeur est écrasée à chaque itération. Ainsi, la valeur de chaque clé est la dernière qui apparaît dans la boucle - "Estonie".

La solution consiste à supprimer les boucles for imbriquées et à utiliser zip() à la place.

Dictionnaires imbriqués Python avec compréhensions de dictionnaire

nums = [0, 1, 2, 3, 4, 5]
 
dict_nums = {n: {'even': n % 2 == 0,
                 'square': n**2,
                 'cube': n**3,
                 'square_root': n**0.5}
             for n in nums}
 
# Pretty print for ease of reading
>>> pprint(dict_nums)
{0: {'cube': 0, 'even': True, 'square': 0, 'square_root': 0.0},
1: {'cube': 1, 'even': False, 'square': 1, 'square_root': 1.0},
2: {'cube': 8, 'even': True, 'square': 4, 'square_root': 1.4142135623730951},
3: {'cube': 27, 'even': False, 'square': 9, 'square_root': 1.7320508075688772},
4: {'cube': 64, 'even': True, 'square': 16, 'square_root': 2.0},
5: {'cube': 125, 'even': False, 'square': 25, 'square_root': 2.23606797749979}}

C'est là que les compréhensions deviennent puissantes. Nous définissons un dictionnaire dans un dictionnaire pour créer beaucoup d'informations en quelques lignes de code. La syntaxe est exactement la même que ci-dessus mais notre valeur est plus complexe que le premier exemple.

N'oubliez pas que nos paires clé-valeur doivent être uniques et nous ne pouvons donc pas créer un dictionnaire comme celui-ci

>>> nums = [0, 1, 2, 3, 4, 5]
>>> wrong_dict = {'number': num, 'square': num ** 2 for num in nums}
  File "<stdin>", line 1
    wrong_dict = {'number': num, 'square': num ** 2 for num in nums}
                                                    ^
SyntaxError: invalid syntax

Nous ne pouvons définir qu'un seul modèle pour les paires clé-valeur dans une compréhension. Mais si vous pouviez définir plus, cela ne serait pas très utile. Nous écraserions nos paires clé-valeur à chaque itération car les clés doivent être uniques.

Instructions Si-Elif-Sinon

nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
 
# Just the even numbers
even_squares = {n: n ** 2 for n in nums
                if n % 2 == 0}
 
# Just the odd numbers
odd_squares = {n: n ** 2 for n in nums
               if n % 2 == 1}
 
>>> even_dict
{0: 0, 2: 4, 4: 16, 6: 36, 8: 64, 10: 100}
 
>>> odd_dict
{1: 1, 3: 9, 5: 25, 7: 49, 9: 81}

Nous pouvons appliquer des conditions if après l'instruction for. Cela affecte toutes les valeurs sur lesquelles vous itérez.

Vous pouvez également les appliquer à vos définitions de clé et de valeur. Nous allons maintenant créer différentes paires clé-valeur selon qu'un nombre est pair ou impair.

# Use parenthesis to aid readability
different_vals = {n: ('even' if n % 2 == 0 else 'odd')
                  for n in range(5)}
 
>>> different_vals
{0: 'even', 1: 'odd', 2: 'even', 3: 'odd', 4: 'even'}

Nous pouvons devenir très complexes et utiliser des instructions if/else à la fois dans les définitions de clé-valeur et après la boucle for !

# Change each key using an f-string
{(f'{n}_cubed' if n % 2 == 1 else f'{n}_squared'): 
 
# Cube odd numbers, square even numbers
 (n ** 3 if n % 2 == 1 else n ** 2)
 
# The numbers 0-10 inclusive
 for n in range(11)
 
# If they are not multiples of 3
 if n % 3 != 0}
 
{'1_cubed': 1, '2_squared': 4, '4_squared': 16, '5_cubed': 125, '7_cubed': 343, '8_squared': 64, '10_squared': 100}

Il est relativement simple de le faire en utilisant des compréhensions. Essayer de le faire avec un constructeur de boucle for ou dict() serait beaucoup plus difficile.

Fusionner deux dictionnaires

Disons que nous avons deux dictionnaires A et B. Nous voulons créer un dictionnaire, C, qui contient toutes les paires clé-valeur de A et B. Comment procédons-nous ?

>>> A = dict(a=1, b=2)
>>> B = dict(c=3, d=4)
 
# Update method does not create a new dictionary
>>> C = A.update(B)
>>> C
>>> type(C)
<class 'NoneType'>
 
>>> A
{'a': 1, 'b': 2, 'c': 3, 'd': 4}

L'utilisation de la fusion ne fonctionne pas. Il modifie A en place et ne renvoie donc rien.

Avant Python 3.5, vous deviez écrire une fonction pour ce faire. Dans Python 3.5, ils ont introduit cette merveilleuse syntaxe.

# Python >= 3.5
>>> A = dict(a=1, b=2)
>>> B = dict(c=3, d=4)
>>> C = {**A, **B}
>>> C
{'a': 1, 'b': 2, 'c': 3, 'd': 4}

Nous utilisons ** avant chaque dictionnaire pour « décompresser » toutes les paires clé-valeur.

La syntaxe est très simple :une liste de dictionnaires séparés par des virgules entourée d'accolades. Vous pouvez le faire pour un nombre arbitraire de dictionnaires.

A = dict(a=1, b=2)
B = dict(c=3, d=4)
C = dict(e=5, f=6)
D = dict(g=7, h=8)
>>> all_the_dicts = {**A, **B, **C, **D}
>>> all_the_dicts
{'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6, 'g': 7, 'h': 8}

Enfin, que se passe-t-il si les dicts partagent des paires clé-valeur ?

>>> A = dict(a=1, b=2)
>>> B = dict(a=999)
>>> B_second = {**A, **B}
>>> A_second = {**B, **A}
 
# Value of 'a' taken from B
>>> B_second
{'a': 999, 'b': 2}
 
# Value of 'a' taken from A
>>> A_second
{'a': 1, 'b': 2}

Comme c'est toujours le cas avec les dictionnaires Python, la valeur d'une clé est dictée par sa dernière affectation. Le dict B_second prend d'abord les valeurs de A puis prend celles de B. Ainsi, toutes les clés partagées entre A et B seront écrasées par les valeurs de B. L'inverse est vrai pour A_seconde.

Remarque :si la valeur d'une clé est remplacée, la position de cette clé dans le dict ne change pas.

>>> D = dict(g=7, h=8)
>>> A = dict(a=1, g=999)
>>> {**D, **A}
 
# 'g' is still in the first position despite being overridden with A's value
{'g': 999, 'h': 8, 'a': 1}

Conclusion

Vous savez maintenant presque tout ce que vous aurez besoin de savoir pour utiliser les dictionnaires Python. Bien fait! Veuillez ajouter cette page à vos favoris et la consulter aussi souvent que nécessaire !

Si vous avez des questions, postez-les dans les commentaires et nous vous répondrons dans les plus brefs délais.

Si vous aimez Python et que vous souhaitez devenir indépendant, il n'y a pas de meilleur cours que celui-ci :

Je l'ai acheté moi-même et c'est pourquoi vous lisez ces mots aujourd'hui.

À propos de l'auteur

Cet article est une contribution de l'utilisateur Finxter Adam Murphy (scientifique des données, grand maître du code Python) :

Je suis un programmeur autodidacte avec un diplôme de première classe en mathématiques de l'Université de Durham et je code depuis juin 2019.

Je connais bien les principes de base du web scraping et de la science des données et je peux vous fournir très rapidement une grande variété d'informations sur le Web.

J'ai récemment récupéré des informations sur toutes les montres que Breitling et Rolex vendent en seulement 48 heures et je suis convaincu que je peux vous fournir des ensembles de données de qualité similaire, quels que soient vos besoins.

Étant de langue maternelle anglaise, mes compétences en communication sont excellentes et je suis disponible pour répondre à toutes vos questions et fournirai des mises à jour régulières sur l'avancement de mon travail.

Si vous souhaitez embaucher Adam, consultez son profil Upwork !

Références

  1. https://www.dictionary.com/
  2. https://tinyurl.com/yg6kgy9h
  3. https://stackoverflow.com/questions/7886355/defaultdictnone
  4. https://www.datacamp.com/community/tutorials/python-dictionary-tutorial
  5. https://docs.python.org/3.8/tutorial/datastructures.html#dictionaries
  6. https://stackoverflow.com/questions/526125/why-is-python-ordering-my-dictionary-like-so
  7. https://stackoverflow.com/a/378987/11829398
  8. https://en.wikipedia.org/wiki/Hash_function
  9. https://docs.python.org/2/library/collections.html#collections.OrderedDict
  10. https://www.quora.com/What-are-hashable-types-in-Python
  11. https://hg.python.org/cpython/file/default/Objects/dictobject.c
  12. https://www.dictionary.com/browse/facetious?s=t
  13. https://thispointer.com/python-how-to-copy-a-dictionary-shallow-copy-vs-deep-copy/
  14. https://docs.python.org/3.8/library/collections.html#collections.Counter
  15. https://stackoverflow.com/questions/12309269/how-do-i-write-json-data-to-a-file
  16. https://realpython.com/python-dicts/#built-in-dictionary-methods
  17. https://stackoverflow.com/questions/38987/how-do-i-merge-two-dictionaries-in-a-single-expression