Prérequis : fonctions en Python , listes et tuples vus en première
À l’issue de ce chapitre, vous saurez :
try/except ou la méthode get ;Plutôt qu’accéder à une valeur à partir de son indice (comme dans un tableau), on peut souhaiter y accéder à partir d’une clé.
Par exemple dans un annuaire téléphonique, on accède à un numéro de téléphone à partir d’un nom. Les noms sont donc les clés, les numéros de téléphone les valeurs. Dans un dictionnaire (au sens traditionnel), on recherche une définition à partir d’un mot. Les mots sont les clés et les définitions les valeurs.
L’accès à la valeur ainsi que la modification ou l’ajout d’une valeur doivent être possibles sans parcourir toute la structure. On parle aussi de tableaux associatifs.
Un dictionnaire est un ensemble de couples (clé, valeur) : on accède à une valeur par sa clé et non par sa position (l’ordre d’insertion est conservé, mais il ne joue aucun rôle). On peut ajouter des couples ; si la clé figure déjà dans le dictionnaire alors le couple est remplacé par le nouveau. Un dictionnaire Python est un objet mutable (comme les listes mais contrairement aux chaînes de caractères ou aux tuples).
Plusieurs méthodes permettent de créer un dictionnaire.
d = dict()
# ou
d = {}
print(d)
{}
On écrit directement les couples clé: valeur entre accolades.
boxoffice = {'Aladdin' : 569439,
'Godzilla 2' : 342574,
'Rocketman' : 277017}
print(boxoffice)
{'Aladdin': 569439, 'Godzilla 2': 342574, 'Rocketman': 277017}
La fonction dict transforme une liste de couples (clé, valeur) en dictionnaire.
couples = [('Aladdin', 569439), ('Godzilla 2', 342574), ('Rocketman', 277017)]
boxoffice = dict(couples)
print(boxoffice)
{'Aladdin': 569439, 'Godzilla 2': 342574, 'Rocketman': 277017}
dict1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
double_dict1 = {k: v * 2 for (k, v) in dict1.items()}
print(double_dict1)
{'a': 2, 'b': 4, 'c': 6, 'd': 8, 'e': 10}
print(boxoffice['Aladdin'])
569439
print(len(boxoffice) == 3)
True
print('Rocketman' in boxoffice)
True
boxoffice['Avengers'] = 0
print(len(boxoffice) == 4)
print(boxoffice['Avengers'])
True
0
boxoffice['Avengers'] = 132260
print(len(boxoffice) == 4)
print(boxoffice['Avengers'])
True
132260
boxoffice['Mon Film'] = 132260
print(len(boxoffice) == 5)
print(boxoffice['Avengers'])
print(boxoffice['Mon Film'])
True
132260
132260
del boxoffice['Mon Film']
print(len(boxoffice) == 4)
print('Mon Film' in boxoffice )
True
False
for film in boxoffice:
print(f"{film} => {boxoffice[film]} spectateurs")
Aladdin => 569439 spectateurs
Godzilla 2 => 342574 spectateurs
Rocketman => 277017 spectateurs
Avengers => 132260 spectateurs
On obtient le même résultat avec le code ci-dessous :
for film in boxoffice.keys():
print(f"{film} => {boxoffice[film]} spectateurs")
Aladdin => 569439 spectateurs
Godzilla 2 => 342574 spectateurs
Rocketman => 277017 spectateurs
Avengers => 132260 spectateurs
for nbre_spectateurs in boxoffice.values():
print(nbre_spectateurs)
569439
342574
277017
132260
for film, nbre_spectateurs in boxoffice.items():
print(f"{film} => {nbre_spectateurs} spectateurs")
Aladdin => 569439 spectateurs
Godzilla 2 => 342574 spectateurs
Rocketman => 277017 spectateurs
Avengers => 132260 spectateurs
print(boxoffice['Mon Film'])
Traceback (most recent call last):
File "main.py", line 1, in <module>
print(boxoffice['Mon Film'])
KeyError: 'Mon Film'
On peut intercepter l’erreur avec les instructions try et except. Python exécute d’abord le bloc try ; si
une erreur KeyError s’y produit, il abandonne ce bloc et exécute le bloc except KeyError au lieu d’arrêter le
programme. Si aucune erreur ne se produit, le bloc except est ignoré.
def nombre_entrees(titre_film):
'''
renvoie le nombre d'entrées du film selon boxoffice, 0 si nombre non connnu
(boxoffice considéré ici comme global)
'''
try:
return boxoffice[titre_film]
except KeyError:
return 0
print(nombre_entrees('Avengers'))
print(nombre_entrees('Mon film'))
132260
0
print(boxoffice.get('Avengers'))
132260
boxoffice.get(key) est équivalent boxoffice[key] si key est une clef qui existeboxoffice.get(key) renvoie None par défaut en cas de clef absentelistend
print(boxoffice.get('Mon Film') == None)
# On peut préciser, en second paramètre, la valeur que l'on veut avoir pour une clef absente
print(boxoffice.get('Mon Film', 0))
print(boxoffice.get('Avengers', 0))
True
0
132260
Lorsqu’on cherche une valeur dans une liste, il faut, dans le pire cas, parcourir tous les éléments un par un. La complexité est donc en \(O(n)\) où \(n\) est la taille de la liste.
def recherche_liste(element, liste):
for e in liste:
if e == element:
return True
return False
animaux = ['chat', 'chien', 'poule', 'vache', 'cochon']
print(recherche_liste('vache', animaux))
print(recherche_liste('tigre', animaux))
True
False
Lorsqu’on cherche une clé dans un dictionnaire, Python utilise une table de hachage. Le principe est le suivant : une fonction de hachage transforme la clé en un indice de tableau, ce qui permet d’accéder directement à la valeur associée, sans parcourir toute la structure. La complexité est donc en \(O(1)\) en moyenne.
ages = {'Alice': 17, 'Bob': 16, 'Charlie': 18}
print('Bob' in ages)
print('Diana' in ages)
True
False
Le test d’appartenance in effectue une recherche en \(O(n)\) sur une liste et en \(O(1)\) sur un dictionnaire. Cela se vérifie expérimentalement :
import time
n = 10_000_000
grande_liste = list(range(n))
grand_dict = {i: None for i in range(n)}
t0 = time.time()
print(n - 1 in grande_liste)
t1 = time.time()
print(f"Liste : {t1 - t0:.4f} s")
t0 = time.time()
print(n - 1 in grand_dict)
t1 = time.time()
print(f"Dictionnaire : {t1 - t0:.6f} s")
La différence est spectaculaire : la recherche dans la liste prend quelques centièmes de seconde tandis que celle dans le dictionnaire prend environ une microseconde, soit un facteur de l’ordre de 10 000 pour n = 10 000 000. Ce facteur augmente avec la taille de la structure : la durée de la recherche dans le dictionnaire ne dépend pas de n, alors que celle de la recherche dans la liste est proportionnelle à n.
À retenir. Le choix entre une liste et un dictionnaire a un impact majeur sur les performances d’un programme. Si l’on a besoin de tester fréquemment l’appartenance d’un élément, un dictionnaire est bien plus adapté qu’une liste.
chiffres_arabes_vers_romains = {1: 'I', 10: 'X', 100: 'C'}
print(chiffres_arabes_vers_romains[10])
X
grille = {(0,0): 'T', (0,1): 'S', (1,0): ' ', (1,1): 'T'}
print(grille[(0,0)] == 'T')
True
Ce code provoque une erreur :
d1 = {'s': 3, 8: 'chat', [1, 'chien']: (2, 7)}
print(d1)
Traceback (most recent call last):
File "main.py", line 1, in <module>
d1 = {'s': 3, 8: 'chat', [1, 'chien']: (2, 7)}
TypeError: unhashable type: 'list'
Pas celui-ci :
d2 = {'s': 3, 8: 'chat', (1, 'chien'): (2, 7)}
print(d2)
{'s': 3, 8: 'chat', (1, 'chien'): (2, 7)}
thon = {'espece': 'T', 'gestation': 3}
requin = {'espece': 'S', 'gestation': 2, 'energie': 2}
mer = {(0, 0): thon, (0, 1): None, (1, 0): None, (1, 1): requin}
print(mer[(0, 0)]['espece'])
print(mer[(1, 1)]['energie'])
T
2
(1, [2]), qui contient une liste, ne peut pas servir de clé.boxoffice['Mon Film'] et boxoffice.get('Mon Film', 0) quand la clé n’existe pas ?
boxoffice['Mon Film'] lève une exception KeyError qui interrompt le programme. boxoffice.get('Mon Film', 0) renvoie la valeur par défaut 0 sans erreur. La méthode get est plus sûre quand on n'est pas certain que la clé existe..keys(), .values() ou .items() ; l’absence d’une clé se gère avec try/except KeyError ou la méthode .get().