Infoforall

PYTHON : description rapide des tuples

L'objet typle de Python est un objet :

Cette fiche détaille l'utilisation des tuples qui sont en gros des listes non mutables.

1 - Description des tuples

Voyons d'abord comment créer un tuple.

1a - Déclaration d'un tuple

On définit un tuple à l'aide d'une parenthèse ouvrante optionnelle signalant le début du tuple et on utilise une parenthèse fermante optionnelle pour signaler la fin du tuple.

C'est un objet natif de Python, de la classe tuple.

Les éléments peuvent être de n'importe quel type, comme avec les listes. On peut avoir des tuples de nombres, de strings, de listes, de tuples et on peut mélanger les contenus.

Quelques exemples :

>>> x = (12,200,100)

>>> x

(12, 200, 100)

>>> type(x)

<class 'tuple'>

>>> x = 12,200,100

>>> x

(12, 200, 100)

>>> type(x)

<class 'tuple'>

On voit bien que l'affichage d'un tuple intègre les parenthèses mais qu'on peut le déclarer sans parenthèse. Evitez par contre de la faire, c'est beaucoup moins clair à lire du coup.

>>> y = ('rouge','vert','bleu')

>>> print (y)

('rouge', 'vert', 'bleu')

>>> z = ('café',3,'litres')

>>> print (y)

('café', 3, 'litres')

On voit qu'on peut déclarer des tuples de nombres ou de strings.

On peut également les nommer des 3-uplets ici puisqu'ils comportent trois éléments.

On peut également avoir différents types d'éléments : z contient des nombres et des strings par exemple.

>>> v = (x,y,z)

>>> v

((12, 200, 100), ('rouge', 'vert', 'bleu'), ('café', 3, 'litres'))

Enfin, on voit qu'on peut même mettre des tuples dans un tuple.

Voyons le cas particulier du tuple vide ou du tuple ne contenant qu'un seul élement :

>>> aa = ()

>>> aa

()

>>> type(aa)

<class 'tuple'>

>>> aa = (5,)

>>> aa

(5,)

>>> type(aa)

<class 'tuple'>

Attention donc de bien placer la virgule en plus des parenthèes : en réalité, c'est la virgule qui indique à l'interpréteur que c'est un tuple.

On peut transformer un autre objet en tuple en utilisant la fonction native tuple(x) où x est l'objet que vous voulez transformer. Cette fonction envoie un résultat qui dépend de l'objet.

>>> x = [1,2,3]

>>> x

[1, 2, 3]

>>> type(x)

<class 'list'>

>>> x = tuple(x)

>>> x

(1, 2, 3)

>>> type(x)

<class 'tuple'>

La présence de crochets [ ] ou de parenthèses ( ) permet de faire la différence entre listes et tupes mais la fonction type() permet de voir clairement les choses. On peut donc transformer les listes en tuples avec tuple() et inversement avec la fonction list().

Il est temps de voir comment lire le contenu d'un tuple.

1b - Objet ordonné et itérable

Jusqu'à présent, nous avions utilisé les tuples surtout pour récupérer les couleurs RBG des pixels des images. Le code ressemblait à ceci :

r,g,b = monImage.getpixel(x,y)

La méthode getpixel renvoie un tuple qui contient les intensités utilisées pour représenter la couleur du pixel.

Si l'image possède 3 couches (RGB), il faut donc trois variables pour récupérer le contenu : r pour l'élément 0, g pour l'élément 1 et b pour l'élément 2.

Nous aurions pu également utiliser quelque chose comme :

tCouleur = monImage.getpixel(x,y)

r,g,b = tCouleur

Ou encore rajouter les parenthèses autour de r,g et b pour indiquer qu'on reçoit un tuple :

tCouleur = monImage.getpixel(x,y)

(r,g,b) = tCouleur

D'ailleurs, il faut faire attention car il est possible que votre image contienne 4 couches : les 3 couches RGB et la couche alpha de transparence. Dans ce cas, il faudra noter :

tCouleur = monImage.getpixel(x,y)

r,g,b,a = tCouleur

Il est même possible que tCouleur ne recoive qu'une seule valeur si l'image est une image en nuance de gris qui ne contient qu'une couche nommée 'L' si vous vous souvenez.

La bonne méthode est donc de d'abord lire tCouleur et de chercher ensuite à savoir si c'est un simple nombre, si c'est un tuple et, si c'est le cas, de connaitre le nombre de couches (voir la fonction len() plus bas).

Une possibilité de correction (avec une simulation de réponse d'un tCouleur) pourrait être :

tCouleur = (45,56,86)

r = g = b = a = False

if type(tCouleur) == tuple :

    if len(tCouleur) == 4 :

            r,g,b,a = tCouleur

    elif len(tCouleur) == 3 :

            r,g,b = tCouleur

else:

    r = tCouleur

On crée 4 variables qui contiennent False. On remplit r avec la valeur de l'intensité s'il n'y a qu'une couche, sinon on remplit les r,g,b et a en fonction du nombre de couches.

On notera que le test sur le type se fait directement avec tuple et pas 'tuple' car la réponse de type(tCouleur) n'est pas un string : il n'y a pas de guillemets.

On peut faire beaucoup plus concis. Le but est de vous montrer comment on peut tester et récupérer manuellement le contenu d'un tuple.

Mais un tuple est un ensemble ordonné d'éléments : on peut y accéder en utilisant un index chiffré.

En effet, on peut accéder au contenu de l'index x en utilisant des crochets : [i].

Attention : l'élément du début est numéroté 0.

>>> x = (12,200,100)

>>> x[0]

12

>>> x[1]

200

On peut connaître la longueur (le nombre d'éléments) d'un tuple en utilisant la fonction générale len(x) où x est l'objet à étudier.

>>> print(len(x))

3

On voit bien que x = (12,200,100) contient 3 éléments( numerotés de 0 à 2, attention) :

  • [0] contient 12
  • [1] contient 200
  • [2] contient 100

On peut alors lire le contenu du tuple en utilisant une boucle for associée à la longueur :

>>> x = (12,200,100)

>>> for i in range(len(x)):

...    print(x[i])

...

12

200

100

Ce n'est pas le moyen le plus simple de lire les éléments un par un. On peut également lire le contenu d'un tuple en utilisant une boucle for nominative : on dit que le tuple est itérable.

>>> x = (12,200,100)

>>> for e in x:

...    print(e)

...

12

200

100

Attention : ici e ne contient pas le numéro de l'élément mais bien l'élément lui-même.

1c - Lecture rapide des caractères d'un tuple

C'est le même fonctionnement que pour les listes et les strings. Vous pouvez passer votre chemin si vous savez gérer les intervalles fournis via les crochets.

Pour lire le contenu des éléments de 0 à 2 (c'est à dire tant que case < 3), on peut écrire :

x = ("Bonjour", "_les", "_gens", "_!")

for i in range(0,3): # On peut écrire plus simplement in range(3)

    print(x[i])

print()

Bonjour

_les

_gens

On obtient les cases de 0 à 2 (car c'est i<3).

On peut obtenir le tuple permettant d'obtenir ces éléments avec les instructions suivantes :

x = ("Bonjour", "_les", "_gens", "_!")

print(x[0:3]) # On peut écrire plus simplement x[:3]

('Bonjour', '_les', '_gens')

Pour lire le contenu des cases de 2 à 3 (c'est à dire tant que case < 4), on peut écrire :

x = ("Bonjour", "_les", "_gens", "_!")

for i in range(2,4):

    print(x[i])

_gens

_!

On obtient les cases de 2 à 3 (car c'est i<4).

On peut obtenir le tuple ayant le même contenu  :

x = ("Bonjour", "_les", "_gens", "_!")

print(x[2:4])

('_gens', '_!')

Pour lire le contenu des cases de 0 à 9 de deux en deux, on peut écrire :

x = tuple ("Bonjour les gens !")

print(x)

for i in range(2,10,2):

    print(x[i])

('B', 'o', 'n', 'j', 'o', 'u', 'r', ' ', 'l', 'e', 's', ' ', 'g', 'e', 'n', 's', ' ', '!')

n

o

r

l

Je transforme ici le string en tuple avec la fonction tuple.

On obtient les éléments de 2 à 9 (car c'est de i = 2 à i < 10 en comptant de 2 en 2 : 2 - 4 - 6 - 8 ) : norl.

On peut obtenir le typle ayant ce contenu avec les instructions suivantes :

x = tuple ("Bonjour les gens !")

print(x[2:10:2])

('n', 'o', 'r', 'l')

On peut omettre de noter certains paramètres. Il suffit de ne pas le mettre mais de placer le : suivant.

Si on ne place pas la position initiale, l'interpréteur remplacera par 0.

Si on ne place pas la position finale, l'interpréteur ira jusqu'au bout du tuple.

Si on ne place pas la valeur de l'itération, l'interpréteur augmentera de 1 en 1.

Ainsi les cas suivants sont équivalents :

>>> x = tuple ("Bonjour les gens !")

>>> print(x[0:5:1])

('B', 'o', 'n', 'j', 'o')

>>> print(x[0:5])

('B', 'o', 'n', 'j', 'o')

>>> print(x[:5])

('B', 'o', 'n', 'j', 'o')

Et un dernier truc pour la route : comment inverser les éléments d'un tuple ? Il suffit de lui dire de compter non pas en 1 mais en -1 !.

Ici, je ne précise ni le début, ni la fin. J'ai donc noter :: pour indiquer que je donne uniquement la valeur de l'itération.

>>> x = tuple ("Bonjour les gens !")

>>> print(x[::-1])

('!', ' ', 's', 'n', 'e', 'g', ' ', 's', 'e', 'l', ' ', 'r', 'u', 'o', 'j', 'n', 'o', 'B')

Pour lire un tuple à l'envers, on peut même faire mieux encore avec la fonction reversed() qui va renvoyer les index dans le sens inverse. C'est une fonction optimisée pour parcourir un objet itérable en sens inverse, elle sera donc normalement plus rapide que la méthode précédente qui est plus généraliste.

x = tuple ("Bonjour les gens !")

for c in reversed(x) :

    print(c)

print()

Par contre, pour créer un nouveau tuple inversé, y = x[::-1] est plus adapté que de passer par la boucle.

1d - Le tuple est non-mutable

Cela veut dire qu'on ne peut pas modifier un tuple après sa création.

Première conséquence : pas de modification possible par interaction directe :

On ne peut pas modifier un tuple en utilisant un code de ce type :

>>> x = (1,2,3)

>>> x[2] = 30

TypeError: 'tuple' object does not support item assignment

Deuxième conséquence : toute utilisation du même nom de variable suivi d'un signe = crée en réalité un nouveau tuple.

Lorsqu'on "modifie" un tuple par concaténation par exemple, on crée un nouvel objet-tuple :

>>> x = (1,2,3)

>>> id(x)

2746216811016

>>> x = x + (4,)

>>> x

(1,2,3,4)

>>> id(x)

2746216863040

On voit ici clairement qu'on utilise le même nom x mais qu'on fait référence à deux entités différentes.

De la même manière, la copie d'un tuple ne permet pas de suivre les modifications effectuées dessus :

>>> x = (1,2,3)

>>> y = x

>>> id(x)

2746216811016

>>> id(y)

2746216811016

>>> x = x + (4,)

>>> id(x)

2746216863040

>>> id(x)

2746216811016

On voit bien que x et y pointent vers le même objet au début mais que lorsqu'on modifie x, y ne suit pas.

1e - Test d'égalité de deux tuples

On peut comparer deux tuples de deux façons :

  • A l'aide d'un test logique x == y qui teste l'égalité de contenu.
  • A l'aide d'un test x is y qui teste l'égalité d'identité (même objet). On peut dire que is teste si x et y sont deux alias du même objet.

Si vous n'allez pas plus loin dans cette sous-partie, retenez ceci : Pour comparer le contenu de deux tuples, utilisez toujours ==..

>>> x = (1,2,3)

>>> y = (1,2,3)

>>> x == y

True

L'explication est donnée au fil des exemples ci-dessous.

Quelques exemples pour comprendre la différence.

Premier exemple : x et y sont identiques (même objet, donc même contenu) :

>>> x = (1,2,3)

>>> y = x

>>> x == y

True

>>> x is y

True

>>> id(x)

2746216888504

>>> id(y)

2746216888504

Les deux tests donnent donc True.

Deuxième exemple : x et y sont différents en contenu et en adresse. :

>>> x = (1,2,3)

>>> y = (4,5,6)

>>> x == y

False

>>> x is y

False

>>> id(x)

2746216888504

>>> id(y)

2746216888280

Pas de surprise, les deux tests donnent False.

Troisième exemple : x et y sont différents en adresse mais pas en contenu :

On reprend l'exemple précédent mais on rajoute "ty" à y :

>>> x = (1,2,3,4)

>>> y = (1,2,3)

>>> y = y + (4,)

>>> x == y

True

>>> x is y

False

>>> id(x)

2746216888504

>>> id(y)

2746216354120

On voit bien que x et contiennent tous les deux (1, 2, 3, 4) : le test == sur le contenu répond True.

Par contre, x et y ne pointent pas vers la même 'adresse' : ce ne sont pas des alias du même objet. Le test is renvoit donc False.

Donc, attention : pour tester l'égalité du contenu, il faut utiliser ==.

Bref, je répète : le test is ne doit pas, sauf exception bien comprise, être utilisé sur les tuples.

1f - Test d'appartenance

Pour savoir si un tuple x contient un élément, on peut utiliser le test in :

>>> x = (1,2,3)

>>> 1 in x

True

>>> 2 in x

True

>>> "3" in x

False

Comme vous pouvez le voir, il n'y a pas d'autotransformation de type de variable. Si on teste la présence du string '3', on ne teste pas la présence du nombre 3.

2 - Méthodes des tuples

Les tuples peuvent utiliser toutes les éléments des objets séquentiels et itérables mais pas celles des objets mutables.

On retrouve donc les méthodes applicables aux listes, moins celles qui concernent la modification (rajout, suppression d'éléments ...)

Il y a donc peu de méthodes finalement par rapport aux listes (mutables) ou aux strings (non mutables mais spécialisés dans le stockage des caractères).

Par contre, la création d'un tuple est plus rapide que la création d'une liste et la lecture d'éléments dans un tuple est plus rapide que la lecture dans une liste.

Comme en plus on peut convertir l'un en l'autre via les fonctions natives list() et tuple, on peut se rendre compte que ces limitations n'en sont pas vraiment.

Des exemples d'utilisation se trouvent sous le tableau.

Attention : les crochets [ ] indiquent que l'argument qu'on y touve est optionnel.

Méthode Action réalisée
tuple.index(x[, start[, end]])

Renvoie le plus petit numéro d'index correspondant à l'élément x.

Renvoie une exception ValueError si aucune élément ne correspond.

Les valeurs start et end sont optionnelles et se comporte comme un intervalle. Les valeurs par défaut sont 0 et la longueur de la liste.

tuple.count(x)

Renvoie le nombre de fois où l'élément x est présent dans la liste.

Exemple : pour tuple.index(x[, start[, end]])

>>> a = ('a', 'b', 'c', 'd', 'a')

>>> a

('a, 'b', 'c', 'd', 'a')

>>> a.index('a')

0

>>> a.index('d')

3

>>> a.index('z')

ValueError: 'z' is not in tuple

Notez bien qu'on détecte d'abord les éléments ayant le numéro d'index le plus petit. L'exception ValueError devra elle être gérer par l'utilisation d'un try ou d'un count.

Exemple : pour tuple.count(x)

>>> a = ('a', 'b', 'c', 'd', 'a')

>>> a

('a, 'b', 'c', 'd', 'a')

>>> a.count('a')

2

>>> a.count('b')

1

>>> a.count('z')

0