Chapitre 518 minDébutant
Chaînes, listes et tuples en Python
Par Josselyn C., prof de BTS SIO. Mis à jour le .
À la fin de ce chapitre, tu sauras
- extraire une partie d'une chaîne ou d'une liste avec les indices et les tranches ;
- découper, nettoyer et rassembler des chaînes ;
- ajouter, retirer et trier les éléments d'une liste ;
- copier une liste sans piège et déballer un tuple.
3 exercices corrigés1 schéma
Une chaîne (str) est une suite de caractères, une liste (list) une suite modifiable de valeurs, un tuple (tuple) une suite non modifiable : ce sont les trois séquences de Python, et elles partagent la même façon d'accéder aux éléments par indices et par tranches. Ce chapitre présente les indices et les tranches, les méthodes des chaînes qui servent tous les jours (découper, nettoyer, rassembler), les opérations sur les listes, le tri, le parcours avec enumerate et zip, la copie d'une liste et le déballage des tuples.
Indices et tranches
Chaque élément d'une séquence a un indice, qui commence à 0. Les indices négatifs partent de la fin : -1 est le dernier élément. Une tranche s[debut:fin] extrait les éléments de debut inclus à fin exclu, comme range().
>>> nom = "SORBIER"
>>> nom[0]
'S'
>>> nom[6]
'R'
>>> nom[-1]
'R'
>>> nom[2:5]
'RBI'
>>> nom[:3]
'SOR'
>>> nom[3:]
'BIER'
>>> nom[-3:]
'IER'
>>> nom[::-1]
'REIBROS'
>>> nom[7]
Traceback (most recent call last):
File "<python-input-9>", line 1, in <module>
nom[7]
~~~^^^
IndexError: string index out of range
| Écriture | Ce qu'elle donne |
|---|---|
s[i] | L'élément d'indice i |
s[-1] | Le dernier élément |
s[a:b] | Les éléments de a inclus à b exclu |
s[:b] | Les b premiers éléments |
s[a:] | Tout à partir de l'indice a |
s[-n:] | Les n derniers éléments |
s[::-1] | La séquence à l'envers |
Un indice hors de la séquence provoque une IndexError, mais une tranche, jamais : nom[5:50] renvoie simplement 'ER'.
Les méthodes des chaînes
Une chaîne est non modifiable : ses méthodes renvoient une nouvelle chaîne, sans toucher à l'originale. Voici celles qu'un script d'administration utilise sans arrêt :
>>> fqdn = "srv-fichiers.ad.sorbier.example"
>>> fqdn.split(".")
['srv-fichiers', 'ad', 'sorbier', 'example']
>>> fqdn.split(".", 1)
['srv-fichiers', 'ad.sorbier.example']
>>> fqdn.upper()
'SRV-FICHIERS.AD.SORBIER.EXAMPLE'
>>> fqdn.startswith("srv-")
True
>>> fqdn.endswith(".example")
True
>>> fqdn.find("sorbier")
16
>>> fqdn.replace("srv-", "")
'fichiers.ad.sorbier.example'
>>> " PC-0042 \n".strip()
'PC-0042'
>>> "42".zfill(4)
'0042'
>>> " ; ".join(["srv-ad", "srv-ad2"])
'srv-ad ; srv-ad2'
>>> "PC-0042".isalnum(), "0042".isdigit()
(False, True)
>>> fqdn[0] = "S"
Traceback (most recent call last):
File "<python-input-12>", line 1, in <module>
fqdn[0] = "S"
~~~~^^^
TypeError: 'str' object does not support item assignment
| Méthode | Rôle | Usage typique |
|---|---|---|
split(sep) | Découpe en liste | Les champs d'une ligne, les morceaux d'un nom DNS |
sep.join(liste) | Rassemble une liste de chaînes | Construire une ligne CSV, une liste lisible |
strip() | Retire espaces et retours à la ligne aux deux bouts | Toute ligne lue dans un fichier ou saisie |
lower(), upper() | Minuscules, majuscules | Comparer sans tenir compte de la casse |
replace(a, b) | Remplace toutes les occurrences | Corriger un séparateur, anonymiser |
startswith(), endswith() | Teste le début ou la fin | Filtrer des noms de machines, des extensions |
find(x) | Indice de la première occurrence, -1 si absente | Repérer un mot (ou x in s pour un simple test) |
zfill(n) | Complète par des zéros à gauche | Numéros d'inventaire PC-0042 |
isdigit(), isalnum() | Que des chiffres ? Que des lettres et chiffres ? | Valider une saisie |
fqdn[0] = "S" échoue : pour « modifier » une chaîne, on en construit une nouvelle, "S" + fqdn[1:]. Les chaînes de Python sont en Unicode : len("Chloé") vaut 5, et "é".upper() donne 'É'.
Les listes : ajouter, retirer, trier
Une liste s'écrit entre crochets ; elle peut contenir des valeurs de tous types, et elle se modifie sur place :
# listes.py : créer, modifier et parcourir une liste
serveurs = ["srv-ad", "srv-fichiers", "srv-gestion"]
serveurs.append("srv-journaux") # ajoute à la fin
serveurs.insert(1, "srv-ad2") # insère à l'indice 1
print(serveurs, len(serveurs))
serveurs.remove("srv-gestion") # retire la première occurrence de cette valeur
dernier = serveurs.pop() # retire et renvoie le dernier élément
print(dernier, serveurs)
print(serveurs[0], serveurs[-1], serveurs[1:3])
print("srv-ad2" in serveurs, serveurs.index("srv-fichiers"))
occupations = [31.7, 87.5, 93.0, 44.8]
print(sum(occupations), min(occupations), max(occupations))
print(sorted(occupations), occupations) # sorted renvoie une nouvelle liste
occupations.sort(reverse=True) # sort trie la liste elle-même
print(occupations)
['srv-ad', 'srv-ad2', 'srv-fichiers', 'srv-gestion', 'srv-journaux'] 5
srv-journaux ['srv-ad', 'srv-ad2', 'srv-fichiers']
srv-ad srv-fichiers ['srv-ad2', 'srv-fichiers']
True 2
257.0 31.7 93.0
[31.7, 44.8, 87.5, 93.0] [31.7, 87.5, 93.0, 44.8]
[93.0, 87.5, 44.8, 31.7]
| Opération | Effet |
|---|---|
liste.append(x) | Ajoute x à la fin |
liste.insert(i, x) | Insère x à l'indice i |
liste.remove(x) | Retire la première occurrence de x (ValueError si absent) |
liste.pop(), liste.pop(i) | Retire et renvoie le dernier élément, ou celui d'indice i |
liste.index(x) | Indice de la première occurrence de x |
x in liste | True si x est dans la liste |
len, sum, min, max | Longueur, somme, minimum, maximum |
liste.sort() | Trie la liste elle-même et renvoie None |
sorted(liste) | Renvoie une nouvelle liste triée, l'originale ne bouge pas |
Parcourir avec enumerate et zip, trier avec une clé
# parcours.py : enumerate, zip et tri avec une clé
noms = ["srv-ad", "srv-fichiers", "srv-gestion", "srv-journaux"]
adresses = ["192.168.10.177", "192.168.10.178", "192.168.10.179", "192.168.10.180"]
for rang, nom in enumerate(noms, start=1):
print(rang, nom)
for nom, ip in zip(noms, adresses):
print(f"{nom:<14}{ip}")
volumes = [("srv-ad", 38), ("srv-fichiers", 437), ("srv-gestion", 112), ("srv-journaux", 186)]
par_taille = sorted(volumes, key=lambda v: v[1], reverse=True)
print(par_taille[:2])
print(sorted(noms, key=len))
1 srv-ad
2 srv-fichiers
3 srv-gestion
4 srv-journaux
srv-ad 192.168.10.177
srv-fichiers 192.168.10.178
srv-gestion 192.168.10.179
srv-journaux 192.168.10.180
[('srv-fichiers', 437), ('srv-journaux', 186)]
['srv-ad', 'srv-gestion', 'srv-fichiers', 'srv-journaux']
enumerate() donne le rang et l'élément à chaque tour, ce qui évite le for i in range(len(liste)) des autres langages. zip() avance dans deux listes en même temps et s'arrête à la plus courte.
Le paramètre key de sorted() dit selon quoi trier : une fonction appliquée à chaque élément. key=len trie les noms par longueur ; key=lambda v: v[1] trie les couples selon leur deuxième élément, la taille. Une lambda est une petite fonction anonyme d'une seule expression : lambda v: v[1] équivaut à une fonction qui reçoit v et renvoie v[1]. Avec reverse=True, le tri est décroissant, et la tranche [:2] garde les deux plus gros volumes.
Copier une liste
Une affectation b = a ne copie pas une liste : elle donne un deuxième nom à la même liste.
>>> a = [3, 8, 5]
>>> b = a
>>> b[0] = 99
>>> a
[99, 8, 5]
>>> c = a.copy()
>>> c[0] = 1
>>> a, c
([99, 8, 5], [1, 8, 5])
Modifier la liste par b modifie donc aussi ce que voit a. Pour une vraie copie, a.copy() (ou list(a), ou a[:]). Le cours Algorithmique illustre ce partage par un schéma (copier une liste). Le même mécanisme explique qu'une fonction qui reçoit une liste peut la modifier : elle reçoit la liste elle-même, pas une copie.
Les tuples et le déballage
Un tuple s'écrit entre parenthèses (ou juste avec des virgules). Comme une chaîne, il est non modifiable : on s'en sert pour un groupe de valeurs qui vont ensemble et ne changent pas, comme un enregistrement ou des coordonnées.
>>> poste = ("PC-0012", "192.168.10.71", "Comptabilité")
>>> poste[1]
'192.168.10.71'
>>> nom, ip, service = poste
>>> service
'Comptabilité'
>>> premier, *reste = ["srv-ad", "srv-ad2", "srv-fichiers"]
>>> reste
['srv-ad2', 'srv-fichiers']
>>> poste[1] = "192.168.10.72"
Traceback (most recent call last):
File "<python-input-6>", line 1, in <module>
poste[1] = "192.168.10.72"
~~~~~^^^
TypeError: 'tuple' object does not support item assignment
Le déballage range chaque élément d'une séquence dans une variable : nom, ip, service = poste. Il faut autant de variables que d'éléments, sauf avec l'étoile, qui ramasse le reste dans une liste. C'est le même mécanisme qui permet à une fonction de renvoyer plusieurs valeurs et à for nom, ip in zip(...) de recevoir deux variables à chaque tour.
Chaîne str | Liste list | Tuple tuple | |
|---|---|---|---|
| Écriture | "abc" | [1, 2, 3] | (1, 2, 3) |
| Contenu | Des caractères | Tout | Tout |
| Modifiable | Non | Oui | Non |
| Usage | Du texte | Une collection qui grandit ou change | Un enregistrement fixe, un retour multiple |
Exercices
Exercice 1 sur 3 Découper une adresse IP Réussi À revoir
Prévois le résultat de chaque ligne, puis vérifie dans la console.
ip = "192.168.10.177"
octets = ip.split(".")
octets
int(octets[3]) + 1
".".join(octets[:3]) + ".0"
ip[:10]
ip.count(".")
Voir la réponseMasquer la réponse de l'exercice 1
>>> ip = "192.168.10.177"
>>> octets = ip.split(".")
>>> octets
['192', '168', '10', '177']
>>> int(octets[3]) + 1
178
>>> ".".join(octets[:3]) + ".0"
'192.168.10.0'
>>> ip[:10]
'192.168.10'
>>> ip.count(".")
3
split renvoie des chaînes : il faut int() pour calculer. octets[:3] garde les trois premiers octets, que join recolle avec des points. Pour de vrais calculs d'adresses (réseau, masque, appartenance), le module ipaddress fait tout cela sans erreur (chapitre 12).
Tu avais trouvé ?
Exercice 2 sur 3 Nettoyer une liste de noms Réussi À revoir
Un collègue a saisi [" SRV-AD ", "srv-ad2\n", "Srv-Fichiers", "srv-gestion "]. Écris un script qui produit la liste des noms sans espaces, en minuscules et triés, puis les affiche séparés par des virgules.
Voir la réponseMasquer la réponse de l'exercice 2
# ex_noms.py : nettoyer et trier une liste de noms de machines
saisis = [" SRV-AD ", "srv-ad2\n", "Srv-Fichiers", "srv-gestion "]
propres = []
for nom in saisis:
propres.append(nom.strip().lower())
propres.sort()
print(propres)
print(", ".join(propres))
['srv-ad', 'srv-ad2', 'srv-fichiers', 'srv-gestion']
srv-ad, srv-ad2, srv-fichiers, srv-gestion
strip() retire les espaces et le \n, lower() uniformise la casse, sort() trie sur place. Le chapitre 6 écrit la même boucle en une ligne avec une compréhension de liste.
Tu avais trouvé ?
Exercice 3 sur 3 Mettre en forme les arrivées Réussi À revoir
Les ressources humaines envoient des lignes au format Prenom;Nom;Service;Fonction :
lignes = [
"Théo;Marchal;Logistique;Préparateur",
"Inès;Roux;Comptabilite;Assistante comptable",
"Zoë;N'Diaye;Logistique;Cariste",
]
Affiche pour chacune NOM Prénom (Fonction, Service), le nom en majuscules.
Voir la réponseMasquer la réponse de l'exercice 3
# ex_arrivees.py : mettre en forme des lignes « Prenom;Nom;Service;Fonction »
lignes = [
"Théo;Marchal;Logistique;Préparateur",
"Inès;Roux;Comptabilite;Assistante comptable",
"Zoë;N'Diaye;Logistique;Cariste",
]
for ligne in lignes:
prenom, nom, service, fonction = ligne.split(";")
print(f"{nom.upper()} {prenom} ({fonction}, {service})")
MARCHAL Théo (Préparateur, Logistique)
ROUX Inès (Assistante comptable, Comptabilite)
N'DIAYE Zoë (Cariste, Logistique)
Le déballage du split(";") donne un nom à chaque champ : plus lisible que champs[0], champs[1]… Si une ligne avait un champ de trop ou de moins, le déballage lèverait une ValueError : un vrai fichier se lit avec le module csv, qui gère aussi les guillemets (chapitre 7).
Tu avais trouvé ?