python tuples

Identification

Infoforall

Fiche Tuples avec Python


L'objet tuple de Python est un objet :

  • ordonné : on peut y accéder avec une boucle for numérique
  • itérable : on peut y accéder avec une boucle for nominative
  • non mutable : en français, on dira non modifiable après création (instanciation).

1 - Déclaration

C'est un objet natif de Python, de la classe 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.

Les éléments peuvent être de n'importe quel type, comme avec les listes. On peut avoir des tuples d'entiers, de floats, 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 le faire, c'est beaucoup moins clair à lire.

>>> y = ('rouge', 'vert', 'bleu') >>> y ('rouge', 'vert', 'bleu') >>> z = ('café', 3, 'litres') >>> y ('café', 3, 'litres')

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

On peut nommer ces exemples des 3-uplets ou des triplets puisqu'ils comportent trois éléments.

On peut également avoir des tuples de tuples.

>>> v = (x,y,z) >>> v ((12, 200, 100), ('rouge', 'vert', 'bleu'), ('café', 3, 'litres'))

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 tuples mais la fonction native type permet de voir clairement les choses. On peut donc transformer les listes en tuples avec tuple et inversement avec la fonction native list.

2 - Objet ordonné et itérable

On peut utiliser les tuples pour récupérer les couleurs RBG des pixels des images. Le code ressemble à 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'.

La bonne méthode est donc de d'abord lire tCouleur et de chercher ensuite à connaitre son nombre d'élément.

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

1 2 3 4 5 6 7 8 9 10
tCouleur = (45, 56, 86) r = g = b = a = None 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 None initialement. 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.

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

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

  • x[0] contient 12
  • x[1] contient 200
  • x[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 element in x: print(element) 12 200 100

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

3 - Lecture rapide des éléments d'un tuple

C'est le même fonctionnement que pour les listes et les strings. Vous pouvez passer votre chemin si vous avez compris le principe sur les deux autres structures étudiées.

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

1 2 3 4
x = ("Bonjour", "_les", "_gens", "_!") for index in range(0,3): # On peut écrire plus simplement in range(3) print(x[index])

Bonjour

_les

_gens

On obtient les éléments d'index de 0 à 2 (car c'est index < 2).

On peut obtenir le tuple contenant ces éléments avec les instructions suivantes :

1 2
x = ("Bonjour", "_les", "_gens", "_!") print(x[0:3]) # On peut écrire plus simplement x[:3]

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

Pour lire éléments d'index 2 à 3 (c'est à dire tant que index < 4), on peut écrire :

1 2 3 4
x = ("Bonjour", "_les", "_gens", "_!") for index in range(2,4): print(x[index])

_gens

_!

On obtient les éléments d'index 2 à 3 (car c'est index <4).

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

1 2
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 :

1 2 3 4 5 6
x = tuple ("Bonjour les gens !") print(x) for index in range(2,10,2): print(x[index])
('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 native tuple.

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

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

1 2 3
x = tuple ("Bonjour les gens !") print(x) print(x[2:10:2])
('B', 'o', 'n', 'j', 'o', 'u', 'r', ' ', 'l', 'e', 's', ' ', 'g', 'e', 'n', 's', ' ', '!') ('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 !") >>> x[0:5:1] ('B', 'o', 'n', 'j', 'o') >>> x[0:5] ('B', 'o', 'n', 'j', 'o') >>> 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 native 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.

1 2 3 4
x = tuple ("Bonjour les gens !") for c in reversed(x): print(c)

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

4 - 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 en utilisant un =, 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(y) 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.

5 - 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 (4,) à 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 y 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 renvoie 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.

6 - 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 de l'integer 3.

7 - Méthodes des tuples

Les tuples peuvent utiliser toutes les méthodes des objets ordonnées 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

Article publié le 11 11 2019
Dernière modification : 11 11 2019
Auteur : ows. h.