11 - Tuples

Introduction

Les tuples (ou n-uplets en français) sont des séquences immutables : on ne peut pas les modifier après leur création. Cette propriété les distingue des listes, qui sont, elles, modifiables.

Le programme de NSI emploie le mot p-uplet : un tuple de p éléments est un p-uplet. Quand les éléments sont repérés par un nom plutôt que par leur position, on parle de p-uplet nommé ; en Python, on le représente par un dictionnaire (chapitre 12).

En mathématiques, on parle de couple pour deux éléments, de triplet pour trois, de quadruplet pour quatre, etc. En informatique, le terme tuple désigne de façon générique un n-uplet, c’est-à-dire une séquence ordonnée de \(n\) éléments.

Création d’un tuple

On crée un tuple en plaçant ses éléments entre parenthèses, séparés par des virgules :

T = ('a', 'b', 'c')
print(T, 'est de type', type(T))
('a', 'b', 'c') est de type <class 'tuple'>

Les parenthèses sont en réalité facultatives : c’est la virgule qui fait le tuple.

T = 'a', 'b', 'c'
print(T, 'est de type', type(T))
('a', 'b', 'c') est de type <class 'tuple'>

On peut aussi créer un tuple à partir d’une autre séquence grâce à la fonction tuple() :

T = tuple(['a', 'b', 'c'])  # à partir d'une liste
print(T, 'est de type', type(T))
('a', 'b', 'c') est de type <class 'tuple'>

Piège classique : le tuple à un seul élément

Attention à la syntaxe pour créer un tuple contenant un seul élément. Les parenthèses seules ne suffisent pas : il faut ajouter une virgule après l’élément.

T = ('a')   # ceci n'est PAS un tuple, mais une chaîne
print(T, 'est de type', type(T))

T = ('a',)  # ceci EST un tuple (remarquez la virgule)
print(T, 'est de type', type(T))
a est de type <class 'str'>
('a',) est de type <class 'tuple'>

À retenir. C’est la virgule qui crée le tuple, pas les parenthèses. Cependant, on préfère toujours utiliser les parenthèses pour la lisibilité.

Le tuple vide

On crée un tuple vide avec des parenthèses vides ou avec tuple() sans argument :

T_vide = ()
print(T_vide, 'est de type', type(T_vide), 'et de longueur', len(T_vide))
() est de type <class 'tuple'> et de longueur 0

Accès aux éléments

On accède aux éléments d’un tuple exactement comme pour une liste, par leur indice (en commençant à 0) :

T = ('a', 'b', 'c', 'd', 'e')
print(T[0])   # premier élément
print(T[2])   # troisième élément
print(T[-1])  # dernier élément
a
c
e

Le slicing fonctionne aussi comme pour les listes :

T = ('a', 'b', 'c', 'd', 'e')
print(T[1:4])   # du deuxième au quatrième
print(T[:3])    # les trois premiers
print(T[::2])   # un élément sur deux
('b', 'c', 'd')
('a', 'b', 'c')
('a', 'c', 'e')

Immutabilité

La caractéristique fondamentale d’un tuple est qu’on ne peut pas modifier ses éléments après sa création. Toute tentative de modification provoque une erreur :

T = ('a', 'b', 'c')
print(T[1])  # on peut lire un élément
b
# Cette instruction génère une erreur
T[1] = 'z'  # on ne peut PAS modifier un élément
TypeError: 'tuple' object does not support item assignment

Pourquoi est-ce utile ? L’immutabilité offre plusieurs avantages :

  • sécurité : on est garanti que les données ne seront pas modifiées accidentellement ;
  • clé de dictionnaire : les tuples peuvent servir de clés dans un dictionnaire, contrairement aux listes (car les clés doivent être immutables).

Exemple concret : pour stocker les cases d’un plateau de jeu, on peut utiliser les coordonnées (ligne, colonne) comme clés d’un dictionnaire :

plateau = {}
plateau[(0, 0)] = "tour"
plateau[(0, 1)] = "cavalier"
plateau[(3, 4)] = "pion"
print(plateau[(0, 0)])
tour

Ceci ne serait pas possible avec des listes [0, 0] car les listes ne peuvent pas être clés de dictionnaire.

Conversion entre listes et tuples

On peut facilement passer d’un type à l’autre grâce aux fonctions tuple() et list() :

L = [1, 2, 3, 'toto']
T = tuple(L)
print('Liste :', L, '-> Tuple :', T)

chaine = 'azerty'
T2 = tuple(chaine)
print('Chaîne :', chaine, '-> Tuple :', T2)

T3 = (10, 20, 30)
L2 = list(T3)
print('Tuple :', T3, '-> Liste :', L2)
Liste : [1, 2, 3, 'toto'] -> Tuple : (1, 2, 3, 'toto')
Chaîne : azerty -> Tuple : ('a', 'z', 'e', 'r', 't', 'y')
Tuple : (10, 20, 30) -> Liste : [10, 20, 30]

Manipulation des tuples

Même si les tuples sont immutables, on peut construire de nouveaux tuples par concaténation et multiplication :

T1 = ('a', 'b', 'c')
T2 = ('d', 'e')
print('T1 + T2 =', T1 + T2)
print('T2 * 3 =', T2 * 3)
T1 + T2 = ('a', 'b', 'c', 'd', 'e')
T2 * 3 = ('d', 'e', 'd', 'e', 'd', 'e')

On peut aussi vérifier l’appartenance d’un élément avec le mot-clé in et connaître la longueur avec len() :

T = ('pomme', 'banane', 'cerise')
print('banane' in T)
print('kiwi' in T)
print('Longueur :', len(T))
True
False
Longueur : 3

Les fonctions min(), max() et sum() fonctionnent également sur les tuples (à condition que les éléments soient comparables ou numériques) :

notes = (12, 15, 8, 17, 14)
print('Min :', min(notes))
print('Max :', max(notes))
print('Somme :', sum(notes))
print('Moyenne :', sum(notes) / len(notes))
Min : 8
Max : 17
Somme : 66
Moyenne : 13.2

Déballage de tuples

Le déballage (ou unpacking) permet d’affecter chaque élément d’un tuple à une variable distincte en une seule instruction :

coordonnees = (3, 7)
x, y = coordonnees
print(f"x = {x}, y = {y}")
x = 3, y = 7

C’est grâce à cette mécanique que l’on peut échanger deux variables en une seule ligne en Python :

a, b = 5, 10
print(f"Avant : a = {a}, b = {b}")
a, b = b, a
print(f"Après : a = {a}, b = {b}")
Avant : a = 5, b = 10
Après : a = 10, b = 5

Attention. Le nombre de variables à gauche doit correspondre exactement au nombre d’éléments du tuple, sinon Python lève une erreur :

T = (1, 2, 3)
a, b = T  # erreur : 3 éléments mais seulement 2 variables
ValueError: too many values to unpack (expected 2)

Tuples et fonctions

Les tuples sont très utilisés pour qu’une fonction renvoie plusieurs valeurs :

def min_max(L):
    """Renvoie le minimum et le maximum d'une liste."""
    return (min(L), max(L))

resultat = min_max([4, 7, 2, 9, 1])
print(resultat)

# Avec le déballage :
petit, grand = min_max([4, 7, 2, 9, 1])
print(f"Le plus petit est {petit}, le plus grand est {grand}")
(1, 9)
Le plus petit est 1, le plus grand est 9

Tuple nommé (pour aller plus loin)

Le module collections propose les namedtuple, qui permettent d’accéder aux éléments par un nom plutôt que par un indice :

from collections import namedtuple

Eleve = namedtuple('Eleve', ['nom', 'prenom', 'classe'])
e = Eleve('Dupont', 'Marie', '1NSI')
print(e.nom, e.prenom, e.classe)
print(e[0])  # l'accès par indice fonctionne toujours
Dupont Marie 1NSI
Dupont

Résumé : tuples vs listes

CaractéristiqueListeTuple
Syntaxe[1, 2, 3](1, 2, 3)
ModifiableOui (muable)Non (immutable)
Clé de dictionnaireNonOui
Cas d’usageCollection évolutiveDonnées fixes