Types séquences : str, tuple, list
Chapitre en cours de relecture
Ce chapitre manque quelques exercices et corrections mineures. Vous pouvez vous y attaquer, mais il faudra y revenir plus tard quand les exercices auront étés ajoutés.
Ce chapitre est également trop détaillé, il doit subir une réécriture.
Un des intérêt des ordinateurs est d'automatiser le traitement de données multiples, voir massives. Par exemple, appliquer un filtre sur les milliers d'images qui composent une vidéo, traiter les requêtes de centaines d'utilisateurs, ou encore analyser des Go de données provenant d'instruments de mesure scientifique.
Pour le moment, on n'a stocké que des "valeurs isolées" dans les variables.
Quand on fait
a
contient un seul entier. Celà permet déjà de faire de nombreux programmes, mais on est quand même relativement limités.
Le premier problème est sémantique : si l'on voulait représenter un vecteur en deux dimensions, \((x,y)\), on devrait utiliser deux variables :
Celà peut vite rendre le code difficile à lire et à faire évoluer.
Ici, puisque les deux coordonnées d'un vecteur vont ensemble, on aimerait pouvoir les grouper dans un même variable, quelque chose comme :
Le second problème est pratique : on ne sait pas toujours à l'avance combien d'éléments on va manipuler. Si on voulait écrire un programme travaillant sur les millions de pixels qui composent une image, il serait déraisonnable d'écrire un programme qui déclare une variable par pixel :
Tous les langages de programmation fournissent une manière de manipuler des données multiples et complexes. En général, celà passe par des types construits.
Idée de types construits
Un type construit est un type de données qui est composé d'autre types de données.
Par exemple, un vecteur en 2D est composé de deux nombres, pour ses deux coordonnées, autrement dit, c'est un couple ou une paire de nombres. Ca pourrait donner quelque chose comme :
ou encore :
Ici paire est un type construit, une paire de nombres. Le *
représente une association, pas une multiplication !
Le nom et le prénom d'un individu pourrait aussi être groupés dans une paire de chaînes :
Dans ce chapitre, on va aborder des types construits que l'on appelle les séquences. Ce sont des types qui ont pour objectif de stocker des données dans un ordre déterminé. En voici trois :
TODO (prof) ajouter schéma ici
str
(chaîne de caractères), une séquence de caractères.tuple
(n-uplet), une séquence de valeurs de types arbitraires.list
(liste ou tableau), une séquence de valeurs de types arbitraires.
Ces trois types séquences supportent globalement les mêmes opérations, on va donc les présenter en même temps. A noter qu'en informatique, une liste et un tableau ne sont pas exactement la même chose. Pour ne pas créer de confusion, on va donc utiliser list
pour désigner le type Python correspondant. De la même manière, on utilisera tuple
pour désigner le type correspondant.
Construction
str
Pour construire une valeur de type chaîne, on écrit des caractères encadrés par des doubles ou des simples quotes:
On peut construire une chaîne vide en n'écrivant aucun caractère entre les quotes:
list
Pour construire une valeur de type list, on spécifie des valeurs encadrées par des crochets []
, et séparées par des virgules.
affiche
On peut aussi utiliser la fonction list
, qui permet de construire une list à partir d'un type séquence :
affiche
On peut construire une list vide en écrivant des parenthèses sans rien au milieu :
affiche
tuple
La construction d'un tuple est très similaire à celle d'une list
, on utilise des parenthèses ()
à la place des crochets []
.
Pour construire une valeur de type tuple, on spécifie des valeurs encadrées par des parenthèses ()
, et séparées par des virgules.
affiche
On peut aussi utiliser la fonction tuple
, qui permet de construire un tuple à partir d'un type séquence :
affiche
On peut construire un tuple vide en écrivant des parenthèses sans rien au milieu :
affiche
Attention, si on veut construire un tuple ne contenant qu'une seule valeur, il faut rajouter une virgule après celle-ci :
affiche
Longueur d'une séquence
La fonction len
(pour LENgth, longueur en anglais) permet de connaître le nombre de valeurs contenues dans un type séquence.
Accès par indice : opérateur []
On peut accéder à un élément donné par sa position, appelée indice (ou parfois index comme en anglais), dans un type séquence en utilisant l'opérateur []
, avec la syntaxe suivante :
Où s
est la valeur de type séquence et i
l'indice de l'élément auquel on veut accéder.
Attention
Le premier élément d'une séquence est à l'indice 0 !
affiche
remarque : l'élément d'une chaîne en python, le caractère, est aussi une chaîne, de longueur 1.
Si l'indice i
donné est négatif, alors la position est comptée à partir de 1 en partant de la fin de la séquence s
(ou plutôt, la position accédée est len(s) + i
) :
affiche
Pour une séquence s
, l'indice i
donné doit vérifier l'encadrement -len(s) <= i < len(s)
, sans quoi une erreur est levée pendant l'opération.
Génère une erreur indice hors limite (index out of range
). Ici, i == len(s)
, donc i < len(s)
n'est pas vérifiée.
Génère une erreur indice hors limite (index out of range
). Ici, i == - len(t) - 1
, donc -len(s) <= i
n'est pas vérifiée.
Exercice : afficher le premier charactère d'une chaine
Implantez une fonction qui prends en entrée une chaîne de caractère non vide et retourne son premier charactère.
- Utilisez l'opérateur
[]
. - Les caractères dans une chaine comm
Exercice : i-ème caractère d'une chaîne
Implantez la fonction suivante :
- Utilisez
[i]
pour accéder au caractère en positioni
dans une chaine.
Appelez la fonction de la manière suivante :
Ce code produit une erreur. Essayez de comprendre pourquoi.
- La dernière ligne d'une erreur Python est généralement la meilleure pour comprendre l'erreur.
- index veut dire indice. C'est un synomyme de position dans notre cas.
- out of range veut dire hors de portée
Index out of range veut dire que l'on essaye d'accéder à une position qui n'est pas dans la chaîne.
Ici, notre chaîne comporte 3 caractères. Comme les indices commencent à zéro, la position la plus grande dans une chaîne de 3 caractères est 2. Donc, quand on essaie d'accéder à la position 8, cette position n'existe pas dans la chaîne, ce qui génère une erreur.
Changez la spécification et ajoutez une assertion pour tenir compte de ce prérequis : la position doit être strictement inférieure à la longueur de la chaîne.
La longueur d'une chaîne se s'obtient avec la fonction len
, qui prends la chaîne en paramètre. len("abc")
vaut 3.
def ieme_caractere(s, i):
"""s une chaine, i un entier, i > 0, i < len(s)
retourne le i-ème caractère de s
ieme_charactere("abc", 2) vaut "c"
"""
assert i < len(s)
return s[i]
Les accès en dehors des positions sont une source fréquente d'erreurs en programmation, il faut donc garder cette possibilité en tête pour débugger plus vite !
Exercice : premier élément d'un tuple
Implantez une fonction qui prends en entier un tuple non vide et retourne son premier élément :
utilisez l'opérateur []
Exercice : i-ème élément d'un tuple
Implantez une fonction qui retourne le i-eme élément d'un tuple :
Utilisez [i]
pour accéder à l'éléménet en position i
dans un tuple
Exercice : premier élément d'une list
Implantez une fonction qui prends en entier une list non vide et retourne son premier élément :
utilisez l'opérateur []
Exercice : i-ème élément d'une list
Implantez une fonction qui retourne le i-eme élément d'une list :
Utilisez [i]
pour accéder à l'éléménet en position i
dans une list
Itération while
En connaissant la longueur d'une séquence et en pouvant accéder à ses éléments un par un, on peut utiliser une boucle while pour faire une opération sur chaque élément. C'est ce qu'on appele parcourir la séquence.
s = "chaine"
i = 0 ## On commence à la position (indice) 0
while i < len(s): ## Tant qu'on a pas dépassé la fin
print(s[i]) ## On fait notre opération
i = i + 1 ## puis on rajoute 1 à la position
affiche :
Exercice : tableau d'itération
Observez le code suivant :
Dressez un tableau qui à chaque ligne récapitule les valeurs de r
et i
à chaque fois que la condition de la boucle est testée.
Voici le début du tableau
i |
r |
---|---|
0 |
"" |
1 |
"c" |
2 |
"ch" |
i |
r |
commentaire |
---|---|---|
0 |
"" |
Ici on rentre dans la boucle |
1 |
"c" |
Premier tour |
2 |
"ch" |
|
3 |
"cha" |
|
4 |
"chai" |
|
5 |
"chain" |
Ici on sort de la boucle |
TODO (prof) ajouter exercices pour tuple et list
Itération for in
Souvent, on n'est pas intéressé par les indices, et on veut seulement parcourir tous les éléments d'une séquence. Dans ce cas là, on peut utiliser une boucle for in, que l'on pourrait traduire par pour chaque dans. Pour une séquence s
, la syntaxe est la suivante :
Ici e
pourrait avoir un autre nom : c'est une variable qui représente successivement chaque élément de la séquence, dans l'ordre croissant de leurs indices. On lit : pour chaque e dans s.
On peut alors réécrire le code de la section précédente plus simplement :
Exercice : inverser une chaîne
Ecrivez une fonction inverse, qui prends en paramètre une chaîne, et retourne une chaîne avec les caractères en ordre inverse
Observez la fonction suivante et adaptez-là :
def copie(s):
""" s une chaine
retourne une copie de s
copie("abcdef") vaut "abcdef"
"""
cop = ""
## pour chaque caractère dans la chaine d'origine
for c in s:
cop = cop + c ## on l'ajoute à la FIN de la copie
return cop
N'hesitez pas à faire un tableau d'itération pour mieux comprendre cette fonction.
Exercice : somme d'un tuple
Implantez la fonction suivante :
Utilisez une boucle for. Il vous faudra aussi utiliser une variable supplémentaire, initialisée à 0.
Exercice : moyenne d'une list
Implantez la fonction suivante :
Utilisez une boucle for.
Rappel : la moyenne d'une séquence d'éléments est la somme des élements divisés par le nombre d'éléments.
Concaténation : opérateur +
L'opérateur +
permet de concaténer deux séquences de même type. Nous l'avons déjà vu pour le type str
.
TODO (prof) ajouter exos ici
Duplicats
Les str
, tuple
et list
peuvent contenir plusieurs fois une même valeur :
Types arbitraires
Les str
ne contiennent que des caractères, des chaînes de longueur 1. En revanche, les tuple
et list
peuvent contenir des valeurs de types différents :
Un tuple
ou une list
peut même contenir d'autres tuple
ou list
!
Attention
Avoir des types arbitraires dans des list
ou des tuple
peut être source d'erreurs. En général, on préférera n'avoir que des valeurs d'un seul type dans une séquence.
Opérateur in
On peut vérifier si une valeur est présente dans une séquence avec l'opérateur in
:
Pour les chaînes, on peut vérifier si chaine est présente.
affiche
Pour tuple
et list
, celà vérifie seulement si la valeur est présente en tant que telle, et non pas si ses éléments sont présents et se suivent :
affiche
Celà vient du fait que (1,2) in (1,2,3)
teste si le tuple
(1,2)
est présent dans (1,2,3)
, et pas si 1 et 2 sont présents et se suivent dans (1,2,3)
.
affiche
Exercice : double consonnes
Implantez la fonction suivante:
Commencez par implanter la fonction suivante :
Utilisez l'opérateur in
pour implanter la fonction contient_double
.
Vous pouvez implanter la fonction double_consonne
en utilisant
une boucle for
pour parcourir la chaine à tester, et donc tester chaque caractère.
def contient_double(c, s):
"""c une chaine contenant un seul caractère. s une chaîne.
retourne True si s contient c + c, False sinon.
exemple :
contient_double("l", "appeller") retourne True
contient_double("t", "appeller") retourne False
"""
return "c" + "c" in s
def double_consonne(s):
"""s une chaine contenant un mot de français écrit sans accent.
retourne True si s contient une double consonne. False sinon.
exemple:
double_consonne("contrepetrie") retourne False
double_consonne("appeller") retourne True
double_consonne("attaque") retourne True
"""
trouve = False
for c in s:
if double_consonne(c,s):
trouve = True
return trouve
def contient_double(c, s):
"""c une chaine contenant un seul caractère. s une chaîne.
retourne True si s contient c + c, False sinon.
exemple :
contient_double("l", "appeller") retourne True
contient_double("t", "appeller") retourne False
"""
return "c" + "c" in s
def double_consonne(s):
"""s une chaine contenant un mot de français écrit sans accent.
retourne True si s contient une double consonne. False sinon.
exemple:
double_consonne("contrepetrie") retourne False
double_consonne("appeller") retourne True
double_consonne("attaque") retourne True
"""
for c in s :
if contient_double(c, s):
## On peut retourner directement dans une boucle,
## Attention toutefois, ça peut baisser la lisibilité
## du code si la boucle est compliquée
return True
On aurait aussi pu ne pas utiliser de fonction intermédiaire. Le code est alors plus rapide à écrire, mais on perds alors l'expressivité qui était donnée par le nom de la fonction itermédiaire
def double_consonne(s):
"""s une chaine contenant un mot de français écrit sans accent.
retourne True si s contient une double consonne. False sinon.
exemple:
double_consonne("contrepetrie") retourne False
double_consonne("appeller") retourne True
double_consonne("attaque") retourne True
"""
for c in s :
if c + c in s:
## On peut retourner directement dans une boucle,
## Attention toutefois, ça peut baisser la lisibilité
## du code si la boucle est compliquée
return True
TODO (prof) ajout version avec tests !
Exercice : est-ce qu'une matière est disponible ?
Implantez un programme qui demande à un utilisateur de rentrer le nom d'une matière, puis dit à l'utilisateur si la matière est disponible ("maths"
ou "NSI"
ou "SVT"
ou "physique"
) ou non. Vous devez utilisez au maximum un if
et un else
.
Exemples de traces :
Utilisez un tuple
(ou une list
) pour stocker la liste des matières !
Utilisez in
pour vérifier si une valeur est dans le tuple.
TODO (prof) ajouter un exo pour list
Egalité : Opérateur ==
L'opérateur ==
permet de vérifier si deux séquences de même types contiennent les mêmes valeurs dans le même ordre.
Si les séquences sont de types différents, l'opérateur renvoie toujours False
!
affiche
L'opérateur s1 != s2
revient à utiliser not (s1 == s2)
.
Exercice : egalité entre list et tuple
Implémentez la fonction suivante :
Vous pouvez utiliser une boucle for ou while pour comparer les deux, mais cette solution est compliquée !
Sinon, vous pouvez transformer la list
en tuple
à l'aide de la fonction tuple
Ou encore, vous pouvez transformer le tuple
en list
avec la fonction list
.
On transforme la list
en tuple
:
def egal(t,l):
"""t un tuple
l une list
retourne True si t contient les mêmes valeurs que l et dans le même ordre, False sinon
Exemple:
egal((1,2,3),[1,2,3]) retourne True
egal((1,2,3),[1,2,3,4]) retourne False
egal((1,2,3),[1,2,2]) retourne False
"""
return t == tuple(l)
ou vice-versa, le tuple
en list
:
Cette solution est beaucoup moins concise, et aussi plus difficile à écrire. La solution concise lui est donc préférable.
def egal(t,l):
"""t un tuple
l une list
retourne True si t contient les mêmes valeurs que l et dans le même ordre, False sinon
Exemple:
egal((1,2,3),[1,2,3]) retourne True
egal((1,2,3),[1,2,3,4]) retourne False
egal((1,2,3),[1,2,2]) retourne False
"""
if len(t) != len(l): ## les deux séquences ne sont pas de même longueur
return False
i = 0
## On va comparer 2 a 2 les éléments de t et l, dans l'ordre.
## tant qu'on trouve des éléments deux à deux égaux, on
## continue, sinon on s'arrête.
while i < len(t) and e[i] == l[i]: ##rappel, ici len(t) == len(l).
i = i + 1
## ici, si i < len(t), alors on n'a pas parcouru tous les éléments, et
## donc on est sorti de la boucle à cause d'une différence
## Si on a pas trouvé de différences (les séquences sont égales),
## alors i == len(t)
return i == len(t)
TODO (prof) ajouter un schéma ?
Tranche : opérateur []
L'opérateur []
sur les séquences permet aussi d'accéder à plusieurs éléments en même temps dans la séquence, en spécifiant un intervalle de positions, de la manière suivante :
On considère que `s = "abcdef"
syntaxe | description | exemple |
---|---|---|
[pos] |
caractère en position pos |
s[1] vaut "b" |
[deb:fin] |
caractères de deb (inclus) à fin (exclus) |
s[2:4] vaut "cd" |
[deb:] |
caractères à partir de deb (inclus) |
s[2:] vaut "cdef" |
[:fin] |
caractères jusqu'à fin (exclus) |
s[:4] vaut "abcd" |
On considère que t = ('a','b','c','d','e','f')
syntaxe | description | exemple |
---|---|---|
[pos] |
élément en position pos |
t[1] vaut ('b') |
[deb:fin] |
éléments de deb (inclus) à fin (exclus) |
t[2:4] vaut ('c', 'd') |
[deb:] |
éléments à partir de deb (inclus) |
t[2:] vaut ('c', 'd', 'e', 'f') |
[:fin] |
éléments jusqu'à fin (exclus) |
t[:4] vaut ('a', 'b', 'c', 'd') |
On considère que l = ('a','b','c','d','e','f')
syntaxe | description | exemple |
---|---|---|
[pos] |
élément en position pos |
l[1] vaut ['b'] |
[deb:fin] |
éléments de deb (inclus) à fin (exclus) |
l[2:4] vaut ['c', 'd'] |
[deb:] |
éléments à partir de deb (inclus) |
l[2:] vaut ['c', 'd', 'e', 'f'] |
[:fin] |
éléments jusqu'à fin (exclus) |
l[:4] vaut ['a', 'b', 'c', 'd'] |
Exercice : i premiers caractères
Implantez la fonction suivante:
Il vous faudra sans doute utiliser l'opérateur []
et des :
. Regardez le tableau dans le cours pour la syntaxe exacte.
Exercice : deux deux
Implantez la fonction suivante :
Vous devrez utiliser l'opérateur []
et des :
. regardez le tableau dans le cours pour la syntaxe exacte.
Vous aurez aussi besoin de l'opérateur +
.
def deux_deux(a, b):
"""a, b des chaines de longueur 4
retourne une chaine composée des deux premiers caractères de a, et des deux derniers caractères de b
deux_deux("abcd", "efgh") vaut "abgh"
"""
assert len(a) == 4 and len(b) == 4
return a[:2] + b[3:]
Alternativement, on aurait pu partir de la fin. de b, en utilisant un indice négatif
Exercice : moite-moite
Implantez la fonction suivante:
La fonction len
permet de connaître la longueur d'une chaîne.
La longueur d'une chaine est un entier.
On peut diviser les entiers par 2.
def moite_moite(a, b):
"""a, b des chaînes non vides
retourne une chaîne composée de la première moitié de a, et de la seconde moitié de b
moite_moite("abcdef", "ghijk") vaut "abcjk"
"""
assert a != "" et b != ""
moit_a = len(a) / 2 ## indice à la moitié de a
moit_b = len(b) / 2 ## incice à la moitié de b
return a[:moit_a] + b[:moit_b]
TODO (prof) ajouter des exercices pour tuple et list ici
Choix du type de séquence
Pour stocker une séquence de caractères, c'est à dire du texte, str
est un choix évident dans la plupart des cas.
Pour des séquences contenant des valeurs d'autres types que des caractères (ou des chaînes), nous sommes obligés d'utiliser tuple
ou list
. Mais comment choisir ?
Jusqu'à présent, on n'a pas vu de différence entre tuple
et list
. Pourtant, il existe une différence majeure :
Les list
peuvent être modifiées, alors que les tuple
ne peuvent pas l'être.
Cette différence est tellement importante qu'elle fera l'objet du prochain chapitre.