1NSI : cours Dictionnaires Python⚓︎
Introduction⚓︎
Dictionnaires / Tableaux Associatifs
Un
nomDictionnaire {
# type hashable : type quelconque
cle1 : valeur1,
cle2 : valeur2,
cle3 : valeur3 # virgule, ou pas (à la dernière ligne)
# etc...
}
Les dictionnaires sont donc indexés par les clés hashables (et non pas par des entiers).
Rappel
- Types Hashables :
int
,float
,str
,tuple
, (frozenset
), ... - Types NON Hashables :
set
,list
,dict
, ...
C'est une structure de données primitive de Python, notée dict
, qui est un type construit donc un conteneur/collection de paires clés : valeurs
ou keys : values
pairs
Un dict
en Python, modélise la notion de fonction définie sur un dictionnaire fini en Mathématiques: ce sont des mappings
Les types des différentes paires clés:valeurs
peuvent :
- être variables, pour certains langages, dits à typage dynamique (Python, ..),
- ou pas, pour certains autres langages, dits à typage statique (langage C, Java, etc..)
Définir un Dictionnaire avec des {}
⚓︎
On définit un dictionnaire de type dict
, avec des accolades {}
# Les clés peuvent être des nombres entiers (car ils sont hashables)
>>> dico = {
# clés : valeurs
13 : "marseille",
69 : "lyon",
29 : "brest"
}
# et même : un mélange de plusieurs types de nombres (entiers et flottants)
>>> dico = {
# clés : valeurs
13 : "marseille",
69 : "lyon",
29 : "brest",
3.14 : "pi"
}
# voire : un mélange de nombres (entiers et flottants), et d'autres types hashables comme 'str'
>>> dico = {
# clés : valeurs
13 : "marseille",
69 : "lyon",
3.14 : "pi",
"marseille" : 13,
"lyon" : 69
}
# voire : on peut même utiliser des tuples en tant que clés (car ils sont hashables)
>>> dico = {
# clés : valeurs
13 : "marseille",
69 : "lyon",
3.14 : "pi",
"marseille" : 13,
"lyon" : 69,
(1,1) : "paul",
("marc","durand") : 27
}
Quels types de paires 'clés-valeurs' peut contenir un dictionnaire ?
- les clés d'un dictionnaire DOIVENT être hashables : types
int
,float
,str
,tuple
En particulier les clés d'undict
ne peuvent PAS être (ni contenir ) de typelist
,set
,dict
(car non hashables) - les valeurs d'un dictionnaire sont de type quelconque
{}
désigne le dictionnaire vide (et non pas un ensemble vide... attention)
Définir un dictionnaire avec dict()
⚓︎
On peut utiliser le constructeur de type dict()
pour définir un dictionnaire :
# à partir d'une suite de paramètres "clé=valeur" :
>>> d1 = dict(one=1,two=2,three=3)
# à partir d'un liste de tuples (clés,valeurs) :
>>> d2 = dict([('one',1),('two',2),('three',3)])
# à partir d'un tuple de tuples (clés,valeurs) :
>>> d3 = dict((('two',2),('one',1),('three',3)))
# à partir d'un ensemble/set de tuples (clés,valeurs) :
>>> d4 = dict({('three',3),('one',1),('two',2)})
# à partir d'un dictionnaire de clés:valeurs :
>>> d5 = dict({'three':3,'one':1,'two':2})
# un mélange des techniques précédentes
>>> d6 = dict({'one':1,'two':2}, three=3)
>>> d1
{'one': 1, 'two': 2, 'three': 3}
>>> d1==d2==d3==d4==d5==d6
True
Remarque : la notation dict()
permet de définir le dictionnaire vide
Plus généralement, on peut générer un dictionnaire grâce à :
* dict(iterable,clé1=valeur1, etc..)
où iterable
est un itérable de tuples
(clés, valeurs) (chaînes, listes, tuples, sets). Les paramètres clé1 = valeur1
sont facultatifs, et doivent être placés à la fin.
* dict(mapping,clé1=valeur1,etc..)
où mapping
est un mapping, comprendre une succession d'associations clés:valeurs
(dictionnaires). Les paramètres clé1 = valeur1
sont facultatifs, et doivent être placés à la fin.
Fonctions utiles sur les dictionnaires⚓︎
Longueur d'un dictionnaire⚓︎
>>> d = {'one': 1, 'two': 2, 'three': 3}
>>> print(d)
{'one': 1, 'two': 2, 'three': 3}
>>> len(d) # renvoie le nombre de paires clés:valeurs du dictionnaire
3
len
renvoie la longueur du dictionnaire d
dictionnaire Vide⚓︎
# {} ou dict() désignent le dictionnaire VIDE
>>> d = {}
>>> d = dict()
>>> len(d)
0 # la longueur du dictionnaire vide vaut 0
{}
ou dict()
représentent le dictionnaire vide, (et NON PAS l'ensemble vide)
Appartenance à un dictionnaire avec in
, ou pas, avec not in
⚓︎
>>> d = {'one': 1, 'two': 2, 'three': 3}
>>> d
{'one': 1, 'two': 2, 'three': 3}
>>> 'one' in d # ATTENTION : ne recherche QUE parmi les clés (sous-entendu)
>>> 'one' in d.keys() # équivalent à la ligne précédente
True
# ou bien NON appartenance avec les mots-clés `not in`
>>> 'one' not in d.keys()
False
>>> 1 in d # ne recherche QUE parmi les clés du dictionnaire (sous-entendu)
False
>>> 1 in d.values() # rechercher parmi les valeurs du dictionnaire
True
# les dictionnaires ne peuvent PAS contenir de dictionnaires en tant que clés...
>>> d = { {1:'a', 2:'b'} :3, 'c':4, 'd':5}
TypeError: unhashable type: 'dict'
# Par contre, rien n'empêche d'avoir un dictionnaire en tant que valeur d'un dictionnaire:
>>> d={3: {1: 'a', 2: 'b'}, 'c':4, 'd':5}
>>> d
{3: {1:'a', 2:'b'}, 'c':4, 'd':5}
Indices / Index⚓︎
Les clés hashables sont les indices des dictionnaires.
>>> d = {'a': 1, 2: 'b', 3: [4,5], (1,2) : 'laurent', 5: {1: 'a', 2: 'b'}}
>>> d['a'] # renvoie la valeur dont la clé est 'a', ici:
1
>>> d[(1,2)] # renvoie la valeur dont la clé est le tuple (1,2), ici:
'laurent'
Pas de slicing évidemment.
Les dictionnaires NE sont PAS des Séquences⚓︎
Les dictionnaires disposent bien d'une fonction len()
, mais PAS d'indices entiers en général: en particulier ils ne sont pas ordonnés (contrairement aux séquences), donc :
Pte
Les dictionnaires dict
NE sont PAS des séquences.
Techniques de parcours de dictionnaires⚓︎
Technique 1 : Parcourir les clés (une par une) avec .keys()
⚓︎
d={'un':1, 'deux':2, 'trois':3}
for cle in d: # Par défaut, la variable 'cle' prend les valeurs des clés du dictionnaire
print(cle)
for cle in d.keys(): # équivalent à la boucle précédente
print(cle)
# Affiche :
# un
# deux
# trois
Pte
Les clés des dictionnaires dict
sont des itérables.
Technique 2 : Parcourir les valeurs (une par une) avec .values()
⚓︎
d = {'un':1, 'deux':2, 'trois':3}
for valeur in d.values(): # équivalent à la boucle précédente
print(valeur)
# Affiche :
# 1
# 2
# 3
Pte
Les valeurs des dictionnaires dict
sont des itérables.
Technique 3 : Parcourir les paires clé:valeur (une par une) avec .items()
⚓︎
d={'un':1, 'deux':2, 'trois':3}
for cle,valeur in d.items(): # équivalent à la boucle précédente
print(cle,valeur)
# Affiche :
# un 1
# deux 2
# trois 3
Pte
Les paires (clés,valeurs) des dictionnaires dict
sont des itérables.
Méthodes sur les dictionnaires⚓︎
Méthodes usuelles⚓︎
Voici quelques exemples d'utilisation de quelques méthodes utiles sur les dictionnaires.
Notons d
un dictionnaire : d={'un':1,'deux':2,'trois':3}
{}.fromkeys(iterable,valeur=None)
crée un dictionnaire dont les clés sont dansiterable
et toutes les valeurs égales àvaleur
(None
par défaut)d.get(cle,default=None)
- renvoie la
valeur
correspondant à lacle
, si elle appartient au dictionnaire - sinon, renvoie
default
(None
par défaut)
- renvoie la
d.pop(cle[,default])
renvoie :- Si la
cle
a été trouvée : supprime lacle
et renvoie lavaleur
correspondante - Si la
cle
n'a pas été trouvée:- renvoie
default
s'il a été donné, - sinon, lève une erreur
KeyError
- renvoie
- Si la
d.popitem()
supprime et renvoie une paire(cle,valeur)
en tant que2-tuple
:- les
2-tuples
sont renvoyés selon la logique LIFO (Last In First Out: Dernier Arrivé Premier Sorti) encore appelé Pile - lève une
KeyError
si le dictionnaire est vide
- les
-
d.copy()
renvoie une copie peu profonde / superficielle ded
ATTENTION : Il existe plusieurs méthodes de copie d'un dictionnaire, avec des propriétés différentes.Ex 1 : si les
valeurs
du dictionnaire sontiterables
alors la modification ded1
entraîne la modification ded2
:On peut éviter ce comportement, de sorte que les deux copies>>> d1 = {'un':[1,2],'trois':3} >>> d2 = d1.copy() # équivalent à d2=d1 >>> d2 {'un':[1,2],'trois':3} >>> d1['un'].append(5) >>> d1 {'un': [1, 2, 5], 'trois': 3} >>> d2 {'un': [1, 2, 5], 'trois': 3}
d1
etd2
soient totalement indépendantes (si tel est votre souhait), en utilisant une copie profonde :>>> import copy >>> d1 = {'un':[1,2],'trois':3} >>> d2 = copy.deepcopy(d1) >>> d2 {'un':[1,2],'trois':3} >>> d1['un'].append(5) >>> d1 {'un': [1, 2, 5], 'trois': 3} >>> d2 {'un': [1, 2], 'trois': 3}
Ex 2 : sinon, si les
valeurs
du dictionnaire ne sont pasiterables
alors la modification ded1
N'entraîne PAS la modification ded2
:>>> d1 = {'un':1,'trois':3} >>> d2 = d1.copy() >>> d2 {'un':1,'trois':3} >>> d1['un'] = 2 >>> d1 {'un': 2, 'trois': 3} >>> d2 {'un': 1, 'trois': 3}
Ex 3 : affectation par Référence avec un =
la modification ded1
entraîne TOUJOURS la modification ded2
:>>> d1 = {'un':1,'trois':3} >>> d2 = d1 >>> d2 {'un':1,'trois':3} >>> d1['un'] = 2 >>> d1 {'un': 2, 'trois': 3} >>> d2 {'un': 2, 'trois': 3}
-
d.update([E,]**F)->None
met à jour le dictionnaired
à partir desdict
/iterables
E
etF
Ex :>>> d1 = {'un':1, 'deux':2} >>> d2 = {'trois':3} >>> d1.update(d2) >>> d1 {'un': 1, 'deux': 2, 'trois': 3} >>> d1.update({'deux':5}) >>> d1 {'un': 1, 'deux': 5, 'trois': 3}
d.clear()
supprime tout ledict
: équivalent àdel d[:]
Liste Complète des Méthodes sur les dictionnaires⚓︎
Aide en ligne⚓︎
Vous trouverez une Liste Complète de Méthodes opérant sur les dictionnaires, sur cette page de la Documentation Officielle
Aide en Local (dans un Interpréteur Python)⚓︎
-
Dans un interpréteur Python,
dir(dict)
affiche la liste complète de toutes les méthodes disponibles sur lesdict
, y compris les méthodes magiques/spéciales (cf ci-dessous), mais elles ne sont pas documentées (ni signature, ni docstring). -
Dans un interpréteur Python,
help(dict)
affiche la liste complète de toutes les méthodes disponibles sur lesdict
, y compris les méthodes magiques/spéciales (cf ci-dessous), AVEC DOCUMENTATION: AVEC LEURS SIGNATURES ET LES DOCSTRINGS.
Méthodes magiques / Méthodes spéciales sur les dictionnaires⚓︎
Méthodes magiques / Méthodes spéciales sur les dictionnaires
Parmi toutes les méthodes disponibles affichées par dir(dict)
, certaines sont encadrées par deux underscores (de chaque côté) __unCertainNom__()
: Elles sont appelées des
- elles sont accessibles via la syntaxe normale pour les méthodes :
nomdictionnaire.__nomMethodeMagique__()
- elles sont également accessibles via une syntaxe spéciale / magique (qui dépend de la méthode en question)
de Méthodes magiques / Méthodes spéciales sur les dictionnaires
On se donne deux dictionnaires d1={'un':1,'deux':2}
et d2={'trois':3,'quatre':4,'cinq':5}
__len()__
: calcule la longueur d'une dictionnaire ...- Syntaxe normale :
d2.__len__()
renvoie le nombre \(3\) - Syntaxe spéciale :
len(d2)
renvoie le nombre \(3\)
- Syntaxe normale :
__eq()__
: teste l'égalité entre deux dictionnaires ...- Syntaxe normale :
d1.__eq__(d2)
renvoieFalse
card1
etd2
ne sont pas égales - Syntaxe spéciale :
d1 == d2
renvoieFalse
(pour les mêmes raisons)
Principe Général : À chaque fois qu'on veut tester l'égalité entre deux dictionnaires avec le symbole==
, c'est en fait la méthode magique__eq__()
qui est appelée pour tester l'égalité.
- Syntaxe normale :
Voici quelques autres méthodes magiques sur les dictionnaires :
__ne__()
veut dire \(\ne\) : "Not Equal to c'est-à-dire Non égal, donc! =
en Python__gt__()
veut dire \(\gt\) : "Greater Than" c'est-à-dire Supérieur Strictement__ge__()
veut dire \(\ge\) : "Greater than or Equal to" c'est-à-dire Supérieur ou égal à__lt__()
veut dire \(\lt\) : "Less Than" c'est-à-dire Inférieur Strictement__le__()
veut dire \(\le\) : "Less than or Equal to" c'est-à-dire Inférieur ou égald.__getitem__(cle)
renvoied[cle]
, i.e. lavaleur
correspondant à lacle
ded
d.__setitem__(cle,valeur)
modifie lavaleur
ded[cle]
. Équivalent àd[cle]=valeur
__contains__()
correspond au mot-cléin
utilisé pour tester l'inclusion d'un dictionnaire dans une autre-
__repr__()
représente un dictionnaire dans un interpréteur Python, c'est-à-dire qu'il affiche un dictionnaire dans un interpréteur Python, sous un certain format spécifique. Elle est appelée quand on tape dans l'interpréteur : - ou bien
>>> d1
\(\quad\) (oùd1
désigne le nom d'un dictionnaire) - ou bien
>>> print(d1)
-
__str__()
représente un dictionnaire dans un interpréteur Python, c'est-à-dire qu'il affiche un dictionnaire dans un interpréteur Python, sous un certain format spécifique, mais seulement pour leprint()
>>> print(d1)
- etc...
Opérations Arithmétiques sur les dictionnaires⚓︎
Addition⚓︎
- PAS d'addition
+
entre deux dictionnaires. - La soustraction
-
existe, et a été définie dans les méthodes.
Multiplication⚓︎
- PAS de produit entre deux dictionnaires, ni entre un dictionnaire et un entier
- PAS de division entre deux dictionnaires
Les dictionnaires sont mutables⚓︎
Par exemple, on peut modifier un dictionnaire dict
in situ, par exemple avec une méthode d'ajout d'élément .add(element)
, EN CONSERVANT LA MÊME ADRESSE MÉMOIRE (qui est en fait un pointeur vers le début du dictionnaire).
>>> d = {'un': 1, 'deux': 5}
>>> id(d) # renvoie l'adresse mémoire du début du dictionnaire
# Exemple de réponse:
140390976336960
# ajout d'élément :
>>> d.update({'deux':2})
>>> id(d) # la 'nouvelle' adresse mémoire du dictionnaire est inchangée
# (ici, modification d'élément)
140390976336960
On s'aperçoit que les deux adresses mémoires, ou pointeurs, AVANT et APRÈS modification du dictionnaire, sont encores égales.
Mutabilité des dictionnaires
Les dictionnaires sont mutables.
Hashabilité (des clés)⚓︎
Pte
Les clés d'un dict
doivent être hashables:
- Types Hashables :
int
,float
,str
,tuple
, (frozenset
), ... - Types NON Hashables :
set
,list
,dict
, ...
Les valeurs d'un dict
peuvent être quelconques.
# un ensemble 'set' ne peut PAS être défini comme clé d'un dictionnaire
>>> d = { {1,2}:3,4:'quatre'}
TypeError: unhashable type: 'set'
# NI une liste 'list' comme clé d'un dictionnaire
>>> d = {[1,2]:'un',3:'trois'}
TypeError: unhashable type: 'list'
# NI un dictionnaire 'dict' comme clé d'un dictionnaire
>>> d={ {1:'un',2:'deux'}:3,4:'quatre'}
TypeError: unhashable type: 'dict'
Compréhensions de dictionnaires⚓︎
Une compréhension de dictionnaires, ou dictionnaire en compréhension, est une syntaxe pour créer/générer un dictionnaire en une seule ligne de commande, en y incluant une boucle for
sur une seule ligne.
Syntaxe sans if
⚓︎
# 'iterable' est un itérable : une chaîne, une liste, un tuple, un dictionnaire, range(), etc...
>>> {fonctionCle(item):fonctionValeur(item) for item in iterable}
>>> dict({fonctionCle(item):fonctionValeur(item) for item in iterable})
Remarque : les chaînes, les listes, les tuples, les dictionnaires, range(), etc... sont des itérables.
Exp
>>> {i:i**2 for i in range(5)}
>>> {i:i**2 for i in [0,1,2,3,4]}
>>> {i:i**2 for i in (0,1,2,3,4)}
>>> dict({i:i**2 for i in (0,1,2,3,4)})
# Renvoient tous le même dictionnaire
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
Syntaxe avec un if
⚓︎
# 'iterable' est un itérable : une chaîne, une liste, un tuple, un dictionnaire, range(), etc...
>>> {fonctionCle(item):fonctionValeur(item) for item in iterable if condition(item)}
>>> dict({fonctionCle(item):fonctionValeur(item) for item in iterable if condition(item)})
Exp
>>> {i:i**2 for i in range(10) if i%4!=0}
>>> dict({i:i**2 for i in range(10) if i%4!=0})
# Renvoient chacune :
{1: 1, 2: 4, 3: 9, 5: 25, 6: 36, 7: 49, 9: 81}
if
DOIT être placé APRÈS le for
, du moins lorsque le if
est tout seul (c'est-à-dire non accompagné d'un else
). En particulier, la syntaxe suivante, que l'on pourrait naïvement croire équivalente, NE FONCTIONNE PAS:
>>> {i:**2 if i%4!=0 for i in range(10)}
>>> dict({i:i**2 if i%4!=0 for i in range(10)})
SyntaxError: invalid syntax
Syntaxe avec un if
ET un else
⚓︎
# 'iterable' est un itérable : une chaîne, une liste, un tuple, un dictionnaire, range(), etc...
>>> {fonctionCle(item):fonctionValeur(item) if condition(item) else autreFonction(item) for item in iterable}
>>> dict({fonctionCle(item):fonctionValeur(item) if condition(item) else autreFonction(item) for item in iterable})
Exp
>>> {i:i**2 if i%4!=0 else "bissextile" for i in range(10)}
>>> dict({i:i**2 if i%4!=0 else "bissextile" for i in range(10)})
# Renvoient chacune :
{0: 'bissextile', 1: 1, 2: 4, 3: 9, 4: 'bissextile', 5: 25, 6: 36, 7: 49, 8: 'bissextile', 9: 81}
if
DOIT être placé APRÈS le for
, du moins lorsque le if
est tout seul (c'est-à-dire non accompagné d'un else
). En particulier, la syntaxe suivante, que l'on pourrait naïvement croire équivalente, NE FONCTIONNE PAS:
>>> {i:i**2 for i in range(21) if i%4!=0 else "bissextile"}
>>> dict({i:i**2 for i in range(21) if i%4!=0 else "bissextile"})
SyntaxError: invalid syntax