Complément - niveau basique¶
Ce document résume les opérations courantes disponibles sur le type dict. On rappelle que le type dict est un type mutable.
Création en extension¶
On l’a vu, la méthode la plus directe pour créer un dictionnaire est en extension comme ceci :
annuaire = {'marc': 35, 'alice': 30, 'eric': 38}
print(annuaire){'marc': 35, 'alice': 30, 'eric': 38}
Création - la fonction dict¶
Comme pour les fonctions int ou list, la fonction dict est une fonction de construction de dictionnaire - on dit un constructeur. On a vu aussi dans la vidéo qu’on peut utiliser ce constructeur à base d’une liste de tuples (clé, valeur)
# le paramètre de la fonction dict est
# une liste de couples (clé, valeur)
annuaire = dict([('marc', 35), ('alice', 30), ('eric', 38)])
print(annuaire){'marc': 35, 'alice': 30, 'eric': 38}
Remarquons qu’on peut aussi utiliser cette autre forme d’appel à dict pour un résultat équivalent :
annuaire = dict(marc=35, alice=30, eric=38)
print(annuaire)The history saving thread hit an unexpected error (OperationalError('attempt to write a readonly database')).History will not be written to the database.{'marc': 35, 'alice': 30, 'eric': 38}
Remarquez ci-dessus l’absence de quotes autour des clés comme marc. Il s’agit d’un cas particulier de passage d’arguments que nous expliciterons plus longuement en fin de semaine 4.
Accès atomique¶
Pour accéder à la valeur associée à une clé, on utilise la notation à base de crochets [] :
print('la valeur pour marc est', annuaire['marc'])la valeur pour marc est 35
Cette forme d’accès ne fonctionne que si la clé est effectivement présente dans le dictionnaire. Dans le cas contraire, une exception KeyError est levée. Aussi si vous n’êtes pas sûr que la clé soit présente, vous pouvez utiliser la méthode get qui accepte une valeur par défaut :
print('valeur pour marc', annuaire.get('marc', 0))
print('valeur pour inconnu', annuaire.get('inconnu', 0))valeur pour marc 35
valeur pour inconnu 0
Le dictionnaire est un type mutable, et donc on peut modifier la valeur associée à une clé :
annuaire['eric'] = 39
print(annuaire){'marc': 35, 'alice': 30, 'eric': 39}
Ou encore, exactement de la même façon, ajouter une entrée :
annuaire['bob'] = 42
print(annuaire){'marc': 35, 'alice': 30, 'eric': 39, 'bob': 42}
Enfin pour détruire une entrée, on peut utiliser l’instruction del comme ceci :
# pour supprimer la clé 'marc' et donc sa valeur aussi
del annuaire['marc']
print(annuaire){'alice': 30, 'eric': 39, 'bob': 42}
Pour savoir si une clé est présente ou non, il est conseillé d’utiliser l’opérateur d’appartenance in comme ceci :
# forme recommandée
print('john' in annuaire)False
Parcourir toutes les entrées¶
La méthode la plus fréquente pour parcourir tout un dictionnaire est à base de la méthode items ; voici par exemple comment on pourrait afficher le contenu :
for nom, age in annuaire.items():
print(f"{nom}, age {age}")alice, age 30
eric, age 39
bob, age 42
On remarque d’abord que les entrées sont listées dans le désordre, plus précisément, il n’y a pas de notion d’ordre dans un dictionnaire ; ceci est dû à l’action de la fonction de hachage, que nous avons vue dans la vidéo précédente.
On peut obtenir séparément la liste des clés et des valeurs avec :
for cle in annuaire.keys():
print(cle)alice
eric
bob
for valeur in annuaire.values():
print(valeur)30
39
42
La fonction len¶
On peut comme d’habitude obtenir la taille d’un dictionnaire avec la fonction len :
print(f"{len(annuaire)} entrées dans annuaire")3 entrées dans annuaire
Pour en savoir plus sur le type dict¶
Pour une liste exhaustive reportez-vous à la page de la documentation Python ici :
https://
Complément - niveau intermédiaire¶
La méthode update¶
On peut également modifier un dictionnaire avec le contenu d’un autre dictionnaire avec la méthode update :
print(f"avant: {list(annuaire.items())}")avant: [('alice', 30), ('eric', 39), ('bob', 42)]
annuaire.update({'jean':25, 'eric':70})
list(annuaire.items())[('alice', 30), ('eric', 70), ('bob', 42), ('jean', 25)]dictionnaire et ordre d’insertion¶
Attention : ce qui suit est valable pour les versions de Python-3.7 et supérieures
depuis cette version un dictionnaire est ordonné; cela signifie qu’il se souvient de l’ordre dans lequel les éléments ont été insérés, et c’est dans cet ordre que l’on itère sur le dictionnaire.
# quand on itère sur un dictionnaire,
# les clés sont parcourues dans l'ordre d'insertion
d = {'a' : 1, 'b' : 2, 'c' : 3}
d['d'] = 4
for k, v in d.items():
print(k, v)a 1
b 2
c 3
d 4
Je vous signale à toutes fins utiles, dans le module collections la classe OrderedDict, qui est une personnalisation (une sous-classe) du type dict, date de l’époque (jusque 3.7 donc) où le dictionnaire natif n’avait pas cette bonne propriété, et qui reste disponible pour des raisons de compatibilité ascendante.
collections.defaultdict : initialisation automatique¶
Imaginons que vous devez gérer un dictionnaire dont les valeurs sont des listes, et que votre programme ajoute des valeurs au fur et à mesure dans ces listes.
Avec un dictionnaire de base, cela peut vous amener à écrire un code qui ressemble à ceci :
# imaginons qu'on a lu dans un fichier des couples (x, y)
tuples = [
(1, 2),
(2, 1),
(1, 3),
(2, 4),
]# et on veut construire un dictionnaire
# x -> [liste de tous les y connectés à x]
resultat = {}
for x, y in tuples:
if x not in resultat:
resultat[x] = []
resultat[x].append(y)
for key, value in resultat.items():
print(key, value)1 [2, 3]
2 [1, 4]
Cela fonctionne, mais n’est pas très élégant. Pour simplifier ce type de traitement, vous pouvez utiliser defaultdict, une sous-classe de dict dans le module collections :
from collections import defaultdict
# on indique que les valeurs doivent être créées à la volée
# en utilisant la fonction list
resultat = defaultdict(list)
# du coup plus besoin de vérifier la présence de la clé
for x, y in tuples:
resultat[x].append(y)
for key, value in resultat.items():
print(key, value)1 [2, 3]
2 [1, 4]
Cela fonctionne aussi avec le type int, lorsque vous voulez par exemple compter des occurrences :
compteurs = defaultdict(int)
phrase = "une phrase dans laquelle on veut compter les caractères"
for c in phrase:
compteurs[c] += 1
sorted(compteurs.items())[(' ', 8),
('a', 5),
('c', 3),
('d', 1),
('e', 8),
('h', 1),
('l', 4),
('m', 1),
('n', 3),
('o', 2),
('p', 2),
('q', 1),
('r', 4),
('s', 4),
('t', 3),
('u', 3),
('v', 1),
('è', 1)]Signalons enfin une fonctionnalité un peu analogue, quoiqu’un peu moins élégante à mon humble avis, mais qui est présente avec les dictionnaires dict standard. Il s’agit de la méthode setdefault qui permet, en un seul appel, de retourner la valeur associée à une clé et de créer cette clé au besoin, c’est-à-dire si elle n’est pas encore présente :
# avant
annuaire{'alice': 30, 'eric': 70, 'bob': 42, 'jean': 25}# ceci sera sans effet car eric est déjà présent
annuaire.setdefault('eric', 50)70# par contre ceci va insérer une entrée dans le dictionnaire
annuaire.setdefault('inconnu', 50)50# comme on le voit
annuaire{'alice': 30, 'eric': 70, 'bob': 42, 'jean': 25, 'inconnu': 50}Notez bien que setdefault peut éventuellement créer une entrée mais ne modifie jamais la valeur associée à une clé déjà présente dans le dictionnaire, comme le nom le suggère d’ailleurs.
Complément - niveau avancé¶
Pour bien appréhender les dictionnaires, il nous faut souligner certaines particularités, à propos de la valeur de retour des méthodes comme items(), keys() et values().
Ce sont des objets itérables¶
Les méthodes items(), keys() et values() ne retournent pas des listes (comme c’était le cas en Python 2), mais des objets itérables :
d = {'a' : 1, 'b' : 2}
keys = d.keys()
keysdict_keys(['a', 'b'])Comme ce sont des itérables, on peut naturellement faire un for avec, on l’a vu :
for key in keys:
print(key)a
b
Et un test d’appartenance avec in :
print('a' in keys)True
print('x' in keys)False
Mais ce ne sont pas des listes¶
isinstance(keys, list)FalseCe qui signifie qu’on n’a pas alloué de mémoire pour stocker toutes les clés, mais seulement un objet qui ne prend pas de place, ni de temps à construire :
# construisons un dictionnaire
# pour anticiper un peu sur la compréhension de dictionnaire
big_dict = {k : k**2 for k in range(1_000_000)}%%timeit -n 10000
# créer un objet vue est très rapide
big_keys = big_dict.keys()110 ns ± 23.3 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
# on répète ici car timeit travaille dans un espace qui lui est propre
# et donc on n'a pas défini big_keys pour notre interpréteur
big_keys = big_dict.keys()%%timeit -n 20
# si on devait vraiment construire la liste ce serait beaucoup plus long
big_lkeys = list(big_keys)25.1 ms ± 3.01 ms per loop (mean ± std. dev. of 7 runs, 20 loops each)
En fait ce sont des vues¶
Une autre propriété un peu inattendue de ces objets, c’est que ce sont des vues ; ce qu’on veut dire par là (pour ceux qui connaissent, cela fait fait référence à la notion de vue dans les bases de données) c’est que la vue voit les changements fait sur l’objet dictionnaire même après sa création :
d = {'a' : 1, 'b' : 2}
keys = d.keys()# sans surprise, il y a deux clés dans keys
for k in keys:
print(k)a
b
# mais si maintenant j'ajoute un objet au dictionnaire
d['c'] = 3
# alors on va 'voir' cette nouvelle clé à partir
# de l'objet keys qui pourtant est inchangé
for k in keys:
print(k)a
b
c
Reportez vous à la section sur les vues de dictionnaires pour plus de détails.