Tuples et dictionnaires

Objectifs

  • Découvrir deux nouvelles structures de données : les tuples et les dictionnaires.

Structures Séquentielles

Un structure de données struct est dite séquentielle si tous ses éléments ont une unique place numérotée \(i\), appelée indice, vérifiant \(0\leq i<\mbox{len(struct)}\).

En python, les structures séquentielles disposent (entre autre) des méthodes :

  • len

  • index

et leurs éléments peuvent être obtenu grace à leurs indice :

struct[i]

Vous connaissez pour l’instant deux structures séquentielles :

  • les chaînes de caractères str;

  • les listes list.

Les chaînes de caractères sont des structures séquentielles de données non mutables de caractères, les listes sont des structures séquentielles de données mutables d’éléments de type quelconque (en Python).

Tuples

Les tuples sont un nouveau type de structure de données séquentielle. Comme les listes, les données de ce type peuvent contenir des éléments de type quelconque. Comme les chaînes de caractères, ces données ne sont pas mutables.

Construction en extension

On peut construire un tuple en extension, c’est-à-dire littéralement par énumération de ses éléments :

t = (1, 2, 2.52, "Timoleon")

t est un tuple de longueur quatre. Les indices de ses élèments sont compris entre 0 et 3 :

len(t)
4
t[0]
1
t[3]
'Timoleon'

Un tuple vide se crée avec une paire de parenthèses :

t = ()
len(t)
0

Il y a un problème pour construire un tuple à un élément, en effet les parenthèses servent également aux expressions arithmétiques. Pour ne pas confondre, on utilise une virgule :

t = (3, )
len(t)
1

Construction avec tuple

On peut également construire un tuple grace au constructeur tuple. Il s’agit d’une fonction prenant en paramètre une structure de données itérable, et qui renvoie un nouveau tuple contenant les éléments de l’itérable.

t = tuple("Timoléon")
len(t)
8
t[1]
'i'
t[-1]
'n'

Mutabilité des tuples

Comme les chaînes de caractères, les tuples ne sont pas mutables : une fois un tuple créé, ne peut modifier ses éléments :

>>> t = (3, 1, 5, 1, 5)
>>> t[2] = 4
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-10-1c3c5480cc1a> in <module>
      1 t = (3, 1, 5, 1, 5)
----> 2 t[2] = 4


TypeError: 'tuple' object does not support item assignment

Opérations entre tuples

Comme les chaînes de caractères, les tuples disposent des opérations :

  • concaténation +;

  • répétition *.

Méthodes sur les tuples

Les tuples disposent des méthodes suivantes :

  • t.index(a) : renvoie l’indice du premier élément égal à a ;

  • t.count(a) : renvoie le nombre d’occurrence de a dans t

Affectation multiple

Une utilisation intéressante des tuples est l’affectation multiple.

Supposons que l’on dispose d’un tuple t à deux éléments

t = (3, 7)

Il est possible de déclarer et d’initialiser deux variables a et b avec le contenu de t, en une seule opération :

(a, b) = t
a
3
b
7

On peut même se dispenser des parenthèses :

c , d = t
c
3
d
7

Cette construction est utile dans les fonctions lorsque l’on souhaite renvoyer plusieurs valeurs. Il suffit de renvoyer un tuple contenant ces valeurs, puis d’utiliser l’affectation multiple.

def mon_divmod(a, b):
    """
    :param a: (int) un entier naturel
    :param b: (int) un entier naturel
    :return: (tuple) le couple (q, r) contenant le quotient et le
            reste de la division euclidienne de a par b
    :CU: a >= 0, b > 0
    :Exemples:

    >>> mon_divmod(0,1)
    (0, 0)
    >>> mon_divmod(26, 7)
    (3, 5)
    """
    q = 0
    r = a - b * q
    while r < 0 or r >= b:
        q = q + 1
        r = a - b * q
    return q, r
mon_divmod(26, 7)
(3, 5)
q, r = mon_divmod(26, 7)
q
3
r
5

Structures itérables

En Python, une structure de données struct est dite itérable si on peut écrire une instruction de la forme :

for a in struct:
    ...
---------------------------------------------------------------------------

NameError                                 Traceback (most recent call last)

<ipython-input-22-86a6f2cfbafd> in <module>
----> 1 for a in struct:
      2     ...


NameError: name 'struct' is not defined

Toutes les structures séquentielles sont itérables. Mais il existe des structures itérables qui ne sont pas séquentielles. Nous allons découvrir une telle structure implantée dans python : les dictionnaires.

Les dictionnaires

Les dictionnaires sont des structures de données permettant d’associer une valeur à une clé.

Ainsi une liste de couples comme l = [('banane', 2.59), ('pomme',1.95), ('orange',1.95)] qui associe un prix au kg à des fruits, peut être considérée comme une liste d’association dans laquelle les fruits sont les clés et les prix les valeurs associées.

Dans un dictionnaire, il ne peut y avoir qu’une seule association pour une clé donnée. Dit autrement, dans un dictionnaire les clés sont uniques.

Constructions

Les constructions littérales de dictionnaires sont obtenues par énumération explicite des associations clé-valeur sous la forme cle : valeur, associations séparées par des virgules, le tout entre accolades.

tarifs = {'banane':2.59, 'pomme':1.95, 'orange':1.95}
tarifs
{'banane': 2.59, 'pomme': 1.95, 'orange': 1.95}
type(tarifs)
dict

Une autre construction possible du même dictionnaire utilise la fonction dict.

tarifs = dict([('banane', 2.59), ('pomme',1.95), ('orange',1.95)])

Attention

toute valeur ne peut pas servir de clé. Seules les valeurs non mutables peuvent servir de clé dans une association.

Par exemple, la construction suivante échoue car on tente de construire un dictionnaire avec une association dont la clé est une liste qui est une structure mutable.

{[] : 1}
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-26-1cc35c74f9b5> in <module>
----> 1 {[] : 1}


TypeError: unhashable type: 'list'

Un dictionnaire qui ne contient aucune association est un dictionnaire vide.

Pour obtenir un dictionnaire vide, on peut donner l’expression

{}
{}

ou bien

dict()
{}

Taille d’un dictionnaire

La taille d’un dictionnaire, c’est-à-dire le nombre d’associations qu’il contient, est donnée par la fonction len.

len(tarifs)
3

Consultation

Comme les ensembles, les dictionnaires ne sont pas des structures séquentielles, et leurs éléments ne sont donc pas indexés par des nombres entiers.

En revanche, on accède à la valeur associée à une clé k dans un dictionnaire d grâce à l’expression d[k].

tarifs['banane']
2.59
tarifs['orange']
1.95

Toute tentative d’accéder à la valeur associée à une clé qui n’existe pas se solde par une exception KeyError.

tarifs['fraise']
---------------------------------------------------------------------------

KeyError                                  Traceback (most recent call last)

<ipython-input-32-33ca56f7891a> in <module>
----> 1 tarifs['fraise']


KeyError: 'fraise'

Existence d’une association pour une clé donnée

On vient de voir que l’accès à la valeur associée à une clé n’existant pas déclenche une exception. Il peut donc être utile de vérifier l’existence de la clé dans un dictionnaire. C’est l’opérateur in qui permet cette vérification.

'banane' in tarifs
True
'fraise' in tarifs
False

Modification

Les dictionnaires sont des structures mutables. On peut modifier la valeur associée à une clé.

Par exemple le prix des bananes baisse.

tarifs['banane'] = 2.30
tarifs
{'banane': 2.3, 'pomme': 1.95, 'orange': 1.95}

Ajout d’une association

On peut aussi modifier un dictionnaire en ajoutant une association.

Les fraises arrivent (et sont cheres).

tarifs['fraise'] = 7.5
tarifs
{'banane': 2.3, 'pomme': 1.95, 'orange': 1.95, 'fraise': 7.5}

Suppression d’une association

On peut enfin supprimer une association dans un dictionnaire. On le fait avec l’instruction del.

del tarifs['fraise']
tarifs
{'banane': 2.3, 'pomme': 1.95, 'orange': 1.95}

Itération

Tout commme les listes, les chaînes de caractères, les intervalles et les ensembles, les dictionnaires sont des structures itérables. Il est donc très facile d’effectuer un traitement sur toutes les associations d’un dictionnaire à l’aide d’une boucle pour.

Voici une itération pour imprimer toutes les clés d’un dictionnaire :

>>> for k in tarifs:
       print(k)
banane
pomme
orange

et une autre pour imprimer toutes les valeurs associées :

>>> for k in tarifs:
       print (tarifs[k])
2.3
1.95
1.95

Attention

Les dictionnaires ne sont pas des structures séquentielles. Il n’est donc pas possible dans un traitement itératif des données contenues dans un dictionnaire de s’appuyer sur un quelconque ordre de ces données.

Quelques applications

Représentation de dates

Une date peut être représentée de différentes façons. L’une d’elles est la donnée de trois nombres entiers donnant le jour, le mois et l’année. Ces trois nombres peuvent être rassemblés dans une liste ou un tuple. Mais y a-t-il un ordre canonique dans lequel ranger ces trois nombres dans une liste ou un tuple ? Et une fois cet ordre choisi comment interpréter une liste ou un tuple de trois entiers ? Par exemple, le tuple (1,2,3) représente-t-il le 1er février de l’an 3, ou le 3 février de l’an 1, ou encore l’une des quatre autres interprétations possibles ?

L’utilisation de dictionnaire permet de nommer les valeurs avec des clés significatives enlevant toute ambiguïté. Pour représenter la date d’aujourd’hui, on peut construire un dictionnaire à trois associations de clés 'jour', 'mois' et 'annee'.

aujourdhui = {'jour': 25, 'mois': 2, 'annee': 2016}

Une fois choisie une telle représentation des dates, on peut réaliser des fonctions pour obtenir diverses opérations sur les dates. Par exemple une fonction qui calcule la date du lendemain.

>>> lendemain(aujourdhui)
{ 'jour' : 26, 'mois' : 2, 'annee' : 2016 }

Tabulation d’une fonction

Les dictionnaires peuvent être utilisés pour tabuler des fonctions.

carres = dict([(k, k*k) for k in range(100)])

On peut désormais utiliser le dictionnaire carres pour obtenir le carré de n’importe quel nombre entier compris entre 0 et 99 inclus, sans avoir à calculer ce carré.

Compter

Voici comment il est possible de compter le nombre d’occurrences de chacun des caractères contenus dans une chaîne à l’aide d’un dictionnaire.

s = "la gardienne du campus dessine"
d = dict()
for c in s:
   if c in d:
     d[c] += 1
   else:
      d[c] = 1

for c in d:
    print(c,':', d[c])
l : 1
a : 3
  : 4
g : 1
r : 1
d : 3
i : 2
e : 4
n : 3
u : 2
c : 1
m : 1
p : 1
s : 3