Les séquences
Retour au sommaire
Introduction
- SourceDans les chapitres précédents, nous avons étudié deux types de données particuliers, les listes et les chaînes, qui servent toutes les deux à contenir des éléments dans un ordre précis. Pour les chaînes, ces éléments sont exclusivement des caractères, mais pour les listes cela peut être n'importe quel objet Python.
Certaines méthodes sont possédées par les deux classes, par exemple index ou count. Dans les deux cas, il existe un spécimen particulier appelé liste vide ou chaîne vide. Et, dans les deux cas, on manipule un nombre d'éléments finis (Python ne possède pas de listes infinies, comme on pourrait en trouver dans le langage Haskell), même si ce détail ne nous a pour l'instant pas sauté aux yeux.
Ces points communs invitent à regrouper les deux classes en une même catégorie.
Définitions et utilisation
- SourceBien que les séquences ne constituent pas un vrai type en Python, on appellera comme ça les objets répondant à la définition suivante : une séquence est un ensemble fini d'éléments, ordonné, indexé par des entiers positifs. Cela veut dire qu'un élément d'une séquence est toujours repéré par sa position dans la séquence (premier, deuxième, etc.), un entier naturel appelé index.
Pour accéder à un objet d'index i placé dans une séquence, on utilise des crochets comme dans l'exemple suivant. Notez que ces index commencent à 0.
>>> l = [1, "banane", 53] >>> l[1] 'banane' # La séquence contenait une autre séquence ! >>> l[1][3] # L'objet contenu à l'index 3 de l'objet d'index 1 de l est... 'a'
Le dernier exemple se lit en deux temps : d'abord l[1] renvoie la chaîne "banane", puis on prend l'élément d'index 3 de cette chaîne, qui est le deuxième caractère 'a'.
Les séquences contenant un nombre fini d'éléments, on peut en mesurer la longueur : ceci se fait à l'aide de la fonction len.
>>> len("chaîne")
6
>>> len(["liste", ["composée", "de séquences"]])
2
>>> len("")
0
Lorsque l'on mesure la longueur d'une liste contenant d'autres séquences, on ne parcourt pas ces séquences éléments, mais seulement la principale. Contrairement à ce qui se passe dans le langage Perl par exemple, les listes contenues dans une autre ne sont pas aplaties.
La fonction len fonctionne sur toutes les séquences, quel que soit leur type réel : elle est dite polymorphe. Elle peut par exemple nous servir à parcourir une séquence, c'est à dire examiner les éléments contenus les uns après les autres à l'aide d'une boucle, et leur appliquer une opération particulière.
Par exemple, nous pouvons parcourir une liste et afficher ses éléments les uns après les autres (nous pourrions afficher la liste entière, mais là présentation sera plus claire ainsi) :
liste = ["domino", "banane", "magicien"]
c = 0
cmax = len(liste)
while c < cmax:
print(liste[c]) # On affiche tous les éléments de la liste un par un
c = c + 1
Cette boucle utilise un compteur, comme nous l'avons déjà rencontré dans les chapitres précédents. Vous pouvez essayer de remplacer la liste par une seule chaîne : le code fonctionnera quand même, affichant chaque élément de la chaîne un par un, c'est à dire chaque caractère. Notez qu'il n'y a pas d'erreur quand on le teste sur une séquence vide. Pourquoi ?
Exercices :
-
Ecrivez un code dans lequel une liste composée de nombre est parcourue, et dont chaque élément est affiché après avoir été multiplié par deux.
-
Ecrivez un code qui affiche tous les éléments d'une séquence en commençant par la fin, puis un élément sur deux, puis qui saute un nombre de plus en plus grand d'éléments. Par exemple, si la liste initiale est [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], votre code affichera 1, 2, 4, 7 et 11.
En faisant ces deux exercices et en testant les codes que vous avez écrit, vous avez peut-être rencontré une erreur qui ressemblait à IndexError: list index out of range. Une telle erreur signifie que vous êtes allé chercher trop loin dans votre liste : vous avez par exemple demandé l'élément 12 alors que la liste ne comportait que 9 éléments.
Rappelez-vous que les indices commencent à 0 en Python, et donc qu'il n'est pas possible d'utiliser l'indice N d'une liste contenant N éléments : l'indice maximum est en fait N-1. Essayez, dans l'exemple précédent, de remplacer l'inégalité stricte < par une inégalité large <= : qu'observez-vous ? Pourquoi ?
Affectation d'un élément
- SourceIl est possible, pour certaines séquences, de modifier un élément d'une séquence sans recréer celle-ci. C'est par exemple le cas pour les listes, et la syntaxe pour le faire est assez naturelle :
>>> maListe = [1, 3, 4, 7] >>> maListe[2] = 5 >>> maListe [1, 3, 5, 7]
Par exemple, si nous possédons une liste contenant des chaînes, nous pouvons modifier chacun de ses éléments (un par un) pour que toutes les chaînes soient en majuscules (la méthode upper d'une chaîne nous servira) :
liste = ["une liste", "de", "chaînes"] print("Ancienne liste :", liste) c = 0 cmax = len(liste) while c < cmax: liste[c] = liste[c].upper() c = c + 1 print("Nouvelle liste :", liste)
Exercices :
-
Ecrivez un code qui transforme une liste de chaînes pour que chaque élément soit passé en minuscules.
-
Même chose avec une liste de nombres, pour que chaque élément soit multiplié par deux.
-
Observez maintenant l'exemple précédent, ainsi que les codes de ces deux exercices : ils possèdent beaucoup de points communs. Or, en programmation, on déteste réécrire des codes similaires, car c'est un manque de souplesse. Avez-vous une idée de comment on pourrait faire pour écrire un code assez général pour que les trois cas précédents puissent le réutiliser simplement ?
En revanche, voici ce qui se produit quand l'on essaye de modifier un caractère dans une chaîne qui existe déjà :
>>> chaine = "hello world"
>>> chaine[4]
'o'
>>> chaine[4] = "a"
Traceback (most recent call last):
File "<pyshell#2>", line 1, in <module>
chaine[4] = "a"
TypeError: 'str' object does not support item assignment
Pas glop : on ne peut pas modifier une chaîne déjà définie, alors qu'on pouvait modifier une liste. Contrairement aux listes, les chaînes sont dites immutables. Ceci découle directement de choix internes à Python. En fait, il existe d'autres types destinés à servir de chaînes de caractères mutables, mais nous ne les détaillerons pas pour l'instant.
Tester l'appartenance à une séquence
- SourceAvant d'utiliser la solution toute prête que Python nous propose, nous allons écrire le code nécessaire à la recherche d'un élément au sein d'une séquence. Nous noterons e l'élément cherché, et S la séquence dans laquelle nous cherchons e (une chaîne ou une liste). À la fin du programme, nous voulons qu'une variable ind contienne l'indice d'un élément e trouvé, ou -1 si S ne contient pas e.
Bien sûr, nous devons parcourir la séquence élément par élément, en comparant à chaque fois ces éléments à e. Ceci se fait au moyen d'une simple boucle. On affecte dès le début du programme la valeur -1 à la variable ind : nous ne modifierons celle-ci que si nous trouvons l'élément cherché, donc si nous ne le trouvons pas, le résultat final sera bien -1.
e = 'c'
S = "Le petit chat est mort"
ind = -1
i = 0
N = len(S)
while i < N:
if S[i] == e:
ind = i
i = i + 1
print(ind)
Ce code fonctionne très bien, et vous pouvez d'ailleurs vous amuser à remplacer S par une autre chaîne, ou par une liste, et e par un autre élément. Cependant, il présente le défaut de parcourir la liste en entier, y compris quand on a déjà trouvé ind. En d'autres termes, il effectue systématiquement N comparaisons, même quand e est au début de S. Ce n'est ici pas très gênant, car on ne fait jamais que comparer des caractères entre eux (encore que, sur une chaîne assez longue, la différence risquerait de se remarquer).
On peut corriger cette maladresse en rajoutant un test supplémentaire dans la boucle. On veut que notre nouvelle boucle s'exécute tant qu'on n'a pas déterminé ind, donc tant que cette variable est égale à -1. Cependant, l'autre condition de la boucle reste valable : comme on n'est pas sûr de trouver e dans S, il faut bien prendre garde à ne pas aller chercher trop loin.
e = 'c'
S = "Le petit chat est mort"
ind = -1
i = 0
N = len(S)
while i < N and ind == -1:
if S[i] == e:
ind = i
i = i + 1
print(ind)
Désormais, notre code est plus efficace : certes, si la séquence ne contient pas l'élément cherché, on continue de la parcourir en entier. Mais au moins, si elle le contient, on s'arrête dès qu'on l'a trouvé.
Bien sûr, nous aurions pu utiliser des méthodes comme find ou insert pour déterminer la présence d'un élément. Le passage précédent servait juste à nous faire réfléchir un petit peu. En réalité, Python propose un mot-clef particulier pour tester la présence d'un élément dans une liste : in. Par exemple, e in S renvoie true si e figure dans S au moins une fois, et false sinon.
En réalité, in fait un petit peu plus : il est également capable de déterminer qu'une sous-chaîne figure dans une chaîne (par exemple, "foo" in "football" est vrai). Pour vous exercer, vous devriez essayer d'écrire un code capable de faire une telle vérification.
Une nouvelle boucle : for in
- SourceMaintenant que vous avez compris comment parcourir une séquence à l'aide d'une boucle while et d'un compteur incrémenté pour énumérer les indices des éléments de la séquence, présentons un outil un petit peu plus adapté. C'est un second type de boucle, dont le principe est non pas d'être exécuté aussi longtemps qu'une condition est satisfaite, mais d'énumérer les éléments d'une séquence ou d'un ensemble.
Sa syntaxe est for e in S:, à la suite de quoi vient le code de la boucle, indenté naturellement comme nous le faisions jusqu'à présent. Dans ce code, e désignera tour à tour tous les éléments de la séquence. Un petit exemple :
>>> l = [1, 34.3, "foo", 12]
>>> for c in l:
print(c)
Rien de bien compliqué, donc, mais cela peut se montrer plus pratique, puisque vous n'avez pas de compteur à maintenir. En revanche, c'est également parfois moins adapté puisque... vous n'avez pas de compteur maintenu ! Selon les besoins, il sera donc plus facile d'utiliser while ou for...in.
Exercices : réécrivez le plus de codes possibles exprimés avec while à l'aide d'une boucle for...in.