Aller au contenu

Type associatif : dict

Relecture nécessaire

Ce chapitre a besoin d'une relecture. C'est ok de s'y attaquer.

Dans le TP répertoire des deux chapitres précédents, on a voulu associer par paire le nom d'un d'un contact et son adresse mail, pour pouvoir facilement retrouver l'adresse mail à partir du nom. On a dû implémenter des fonctions de recherche, d'ajout et de modifications spécialisées.

Python propose un type, dict, ou dictionnaire, spécialisé dans la gestion d'associations par paires.

Construction

Quand on recherche un contact, on veut trouver son adresse mail à partir de son nom. On dit que le nom est la clé qui identifie l'adresse mail, et que l'adresse mail est la valeur identifiée.

On construit un dictionnaire en utilisant des accolades {}.

a = {} ## Crée un dictionnaire vide

On peut spécifier des paires sous la forme cle:valeur, séparées par des virgules.

d = {"Alex Emple":"alexemple@unmail.fr",
"Daryl Ustration":"dust@autremail.fr",
"Clara Perçu":"thumbnail@mail.fr"}

Présence d'une clé

On peut tester si une clé est présente dans le dictionnaire avec le mot clé in.

"Alex Emple" in d ## Vaut True
"Felix Bertoni" in d ## Vaut False

Accès à une valeur

On accède à la valeur associée à une clé en utilisant l'opérateur [] avec la syntaxe dictionnaire[cle].

print(d["Alex Emple"])

affiche

alexemple@unmail.fr

Si on essaie d'accéder à une valeur et que la clé demandée n'existe pas, une erreur KeyError est levée :

print(d["Felix Bertoni"])

affiche :

Traceback (most recent call last):
  File "<input>", line 1, in <module>
KeyError: 'Felix Bertoni'

Exercice

Implantez la fonction suivante :

def afficher_valeur(d, c):
    """d un dictionnaire
    c une chaîne
    si c est une clé de d, affiche la valeur associée à c dans d
    sinon, affiche "clé non trouvée"

    >>> afficher_valeur({}, "a")
    clé non trouvée

    >>> afficher_valeur({"a":"b","c":"d:"}, "a")
    b

    >>> afficher_valeur({"a":"b","c":"d:"}, "e")
    clé non trouvée
    """
    pass

Utilisez l'opérateur in pour tester si la clé est présente dans le dictionnaire.

Utilisez l'opérateur [] pour accéder à la valeur associée à la clé.

Vous aurez probablement besoin d'un if ... else.

def afficher_valeur(d, c):
    """d un dictionnaire
    c une chaîne
    si c est une clé de d, affiche la valeur associée à c dans d
    sinon, affiche "clé non trouvée"

    >>> afficher_valeur({}, "a")
    clé non trouvée

    >>> afficher_valeur({"a":"b","c":"d:"}, "a")
    b

    >>> afficher_valeur({"a":"b","c":"d:"}, "e")
    clé non trouvée
    """
    if c in d:
        print(d[c]) 
    else:
        print("clé non trouvée")

Ajout et modification d'une valeur

L'opérateur [] utilisé avec la syntaxe suivante permet à la fois d'ajouter et de modifier des valeurs dans le dictionnaire.

dictionnaire[cle] = valeur

Si cle n'est pas dans dictionnaire, alors la paire clé:valeur est ajoutée au dictionnaire.

d = {}
d["a"] = "b"
print(d)

Affiche

{'a': 'b'}

Si cle est présente dans le dictionnaire, alors sa valeur associée devient valeur.

d = {"a":"b"}
print(d)
d["a"] = "c"
print(d)

Affiche

{'a': 'b'}
{'a': 'c'}

Exercice

Implantez la fonction suivante :

def paires_vers_dict(liste):
    """liste une list de tuples (str,str)
    retourne un dict où les paires de liste sont stockées sous forme clé:valeur.
    Si une clé est trouvée deux fois, alors c'est la 
    dernière valeur correspondante qui est ajoutée.


    >>> paires_vers_dict([])
    {}

    >>> paires_vers_dict([("a","b"), ("c","d")])
    {'a': 'b', 'c': 'd'}

    >>> paires_vers_dict([("a","b"), ("c","d"), ("a","e")])
    {'a': 'e', 'c': 'd'}
    """
    pass
def paires_vers_dict(liste):
    """liste une list de tuples (str,str)
    retourne un dict où les paires de liste sont stockées sous forme clé:valeur.
    Si une clé est trouvée deux fois, alors c'est la 
    dernière valeur correspondante qui est ajoutée.


    >>> paires_vers_dict([])
    {}

    >>> paires_vers_dict([("a","b"), ("c","d")])
    {'a': 'b', 'c': 'd'}

    >>> paires_vers_dict([("a","b"), ("c","d"), ("a","e")])
    {'a': 'e', 'c': 'd'}
    """
    d = {}
    for e in liste:
        d[e[0]] = e[1]
    return d

Parcours

Le dictionnaire a deux méthodes, keys et values qui retournent respectivement les clés et les valeurs.

d = {"a":"b", "c":"d", "e":"f"}
print(d)
print(d.keys())
print(d.values())

affiche

{'a': 'b', 'c': 'd', 'e': 'f'}
dict_keys(['a', 'c', 'e'])
dict_values(['b', 'd', 'f'])

On peut parcourir ces valeurs de retour de ces méthodes avec une boucle for.

d = {"a":"b", "c":"d", "e":"f"}
for k in d.keys():
    print(k)
affiche

a
c
e
et

d = {"a":"b", "c":"d", "e":"f"}
for v in d.values():
    print(v)

affiche

b
d
f

A noter qu'on peut utiliser for ... in ... directement avec un dict. Dans ce cas là, ce sont les clés qui sont parcourues. On peut également utiliser l'élément clé pour accéder à la valeur correspondante.

d = {"a":"b", "c":"d", "e":"f"}
for k in d:
    print(f"{k}:{d[k]}")

affiche

a:b
c:d
e:f

Exercice

Implantez la fonction suivante :

def cles_diff_vals(d):
    """d un dict
    return True si aucune clé de d n'est valeur de d,
    False sinon

    >>> cles_diff_vals({})
    True

    >>> cles_diff_vals({'a':'b', 'c':'d'})
    True

    >>> cles_diff_vals({'a':'a', 'c':'d'})
    False

    >>> cles_diff_vals({'a':'b', 'b':'d'})
    False
    """
    pass

Vous pouvez utiliser l'opérateur in sur les clés et valeurs obtenues avec les méthodes keys et values :

d = {"a":"b"}
print("a" in d.keys())
print("a" in d.values())

affiche

True
False
def cles_diff_vals(d):
    """d un dict
    return True si aucune clé de d n'est valeur de d,
    False sinon

    >>> cles_diff_vals({})
    True

    >>> cles_diff_vals({'a':'b', 'c':'d'})
    True

    >>> cles_diff_vals({'a':'a', 'c':'d'})
    False

    >>> cles_diff_vals({'a':'b', 'b':'d'})
    False
    """
    for k in d:
        if k in d.values():
            return False
    return True

Type des clés

Nous avons pour le moment utilisé des chaines comme clés et valeurs, mais le type dict est moins restrictif.

Le type dict autorise, entre autres, à créer des clés de types entiers, chaînes, flottants, booleen, None, et tuples contenant les six types précédents.

d = {"a":"b", 2:"c", 2.0:"d", True:"e", (1, False):"f", None:"g"}

Dans un même dict, les clés ne sont pas nécessairement du même type. Toutefois, pour une meilleure lisibilité et simplicité du code, on se restreindra à créer et manipuler des dict dont toutes les clés sont du même type.

Type des valeurs

Les valeurs dans un dictionnaire peuvent être de n'importe quel type, et même de types différents.

d = {"nom":"Félix", "age":26}

Sauf dans une exception que nous verrons en fin de chapitre, nous n'utiliserons que des dictionnaires où toutes les clés ont le même type, et toutes les valeurs ont le même type. Par exemple pour lister les notes en NSI des élèves:

notes = {"Felix":18, "Alison":17, "Martin":20}
notes["Alexandre"] = 19

Propriétés

Les clés retounées par la méthode keys() sont dans l'ordre de leur insertion.

Les valeurs retournées par la méthode values() sont dans l'ordre des clés correspondantes.

d = {1:2,3:4,5:6,7:8,9:10}

print(d.keys())
print(d.values())

d[1] = 5

print(d.keys())
print(d.values())

Affiche

dict_keys([1, 3, 5, 7, 9])
dict_values([2, 4, 6, 8, 10])
dict_keys([1, 3, 5, 7, 9])
dict_values([5, 4, 6, 8, 10])

Exercice

Cet exercice est relativement difficile, prenez donc votre temps pour le faire.

Implantez la fonction suivante :

def inverse(d):
    """d un dictionnaire
    retourne un dictionnaire qui contient les mêmes éléments
    que d, mais avec les clés en ordre inverse


    >>> inverse({"a":"b", "c":"d", "e":"f", "g":"h"})
    {'g': 'h', 'e': 'f', 'c': 'd', 'a': 'b'}

    >>> inverse({})
    {}
    """
    pass

Il vous faudra sans doute utiliser une liste itermédiaire pour stocker les clées en ordre inverse.

N'hésitez pas à consulter les exercices du chapitre précédent pour savoir comment on inverse une liste.

def inverse(d):
    """d un dictionnaire
    retourne un dictionnaire qui contient les mêmes éléments
    que d, mais avec les clés en ordre inverse


    >>> inverse({"a":"b", "c":"d", "e":"f", "g":"h"})
    {'g': 'h', 'e': 'f', 'c': 'd', 'a': 'b'}

    >>> inverse({})
    {}
    """
    cles = []
    for k in d:
        cles.insert(0, k)

    d2 = {}
    for c in cles:
        d2[c] = d[c]
    return d2

Les dicts, au même titre que les lists, dont mutables.

d = {"a":"b"}
e = d

print(d)
print(e)

d["a"] = "c"
d["b"] = "d"

print(d)
print(e)

affiche

{'a': 'b'}
{'a': 'b'}
{'a': 'c', 'b': 'd'}
{'a': 'c', 'b': 'd'}

Il convient donc d'être prudent lors de leur manipulation.

Structurer les données

On a vu que les list pouvaient être utiles pour manipuler beaucoup de données avec peu de variables.

On veut aussi parfois grouper et manipuler des données de types différents comme un tout. Jusqu'à présent, on a utilisé soit des variables séparées, soit un tuple.

Par exemple, pour représenter un contact.

Si l'on représente le contact avec plusieurs variables :

nom_contact = "Félix"
age_contact = 26
mail_contact = "mail@mail.fr"
sympa = True

C'est compliqué à lire et on doit à chaque fois gérer toutes les variables.

Si on représente le contact par un tuple :

contact = ("Félix", 26, "mail@mail.fr", True)

Comme l'accès aux éléments se fait à l'aide d'un entier, on a parfois du mal à voir ce que l'on fait si l'on a oublié l'organisation du tuple...

print(contact[2]) # Pas facile de savoir que c'est l'email qui sera affiché.

Utiliser un dict avec des clés de type chaîne résouds les deux problèmes à la fois:

contact = {"nom":"Félix", "age":26, "mail":"mail@mail.fr", "sympa":True}

Ici, une seule variable à manipuler, et l'accès à un élément est clair :

print(contact["mail"])

Cette structuration est la seule exception pour laquelle on acceptera de faire des dict avec des valeurs de types différents. A noter que les clés seront en revanches toutes des chaînes.

Exercice

Représentez le vecteur vec en deux dimensions, de coordonnées x et y valant respectivement 0 et 3 :

  • A l'aide de plusieurs variables
  • A l'aide d'un tuple
  • A l'aide d'un dict

variables :

vec_x = 0
vec_y = 3

tuple :

vec = (0, 3)

dict :

vect = {"x":0, "y":3}

Proposez une fonction addition, qui permet d'additionner deux vecteurs représentés par des variables séparées, et qui retourne un vecteur sous forme de deux valeurs séparées. Utilisez-la pour additionner les vecteurs \((0, 4)\) et \((5,3)\)

Pour rappel, avec \(u = (u_x, u_y)\) et \(v = v_x, v_y)\) deux vecteurs en deux dimensions, on a :

\[ u + v = (u_x + v_x, u_y + v_y) \]

Si vous n'y arrivez pas, cherchez un peu mais n'attendez pas trop pour regarder l'aide, puis la solution.

Une fonction Python peut retourner plusieurs valeurs, avec la syntaxe suivante :

return val1, val2, ...

Les valeurs sont en réalité emballées (packed en anglais) sous formes d'un tuple.

def ret_plusieurs():
    return 1, 0

print(ret_plusieurs())

affiche

(1, 0)

On peut déballer (unpack en anglais) les valeurs avec la syntaxe suivante :

var1, var2, ... = appel_fonction

Par exemple :

a, b = ret_plusieurs()
print(a)
print(b)

affiche

1
0
def addition(vec1_x, vec1_y, vec2_x, vec2_y):
    return vec1_x + vec2_x, vec1_y + vec2_y


vec1_x = 0
vec1_y = 4
vec2_x = 5
vec2_y = 3
vec3_x, vec3_y = addition(vec1_x, vec1_y, vec2_x, vec2_y)

Proposez une fonction addition et utilisez-la pour additionner les vecteurs \((0, 4)\) et \((5,3)\), pour les deux autres représentations proposées partie 1.

tuple :

def addition(vec1, vec2):
    return (vec1[0] + vec2[0], vec1[1] + vec2[1])

vec1 = (0, 4)
vec2 = (5, 3)
vec3 = addition(vec1, vec2)

dict :

def addition(vec1, vec2):
    return {"x":vec1["x"]+vec2["x"], "y":vec2["y"]+vec2["y"]}

vec1 = {"x":0, "y":4}
vec2 = {"x":5, "y":3}
vec3 = addition(vec1, vec2)