freedzhost.cloud

Python module 2 : boucles, listes, dictionnaires, fichiers

Niveau DébutantTemps estimé 14 hÀ lire avant Python module 1 : installer, écrire, faire réagirUn mot inconnu ? Lexique

Quatorze séances pour passer du programme-jouet au programme utile : faire répéter la machine (boucles), stocker des collections de données (listes, dictionnaires), lire et écrire de vrais fichiers, et survivre aux erreurs. À la fin du module, vous saurez lire un fichier CSV ligne par ligne et en tirer des totaux.

Jour 15 — La boucle while : répéter tant que

Objectif : répéter un bloc tant qu'une condition est vraie, sans jamais écrire de boucle infinie par accident.

Théorie (20 min)

Un ordinateur ne se lasse pas : c'est toute sa valeur. La boucle while (« tant que ») répète un bloc indenté tant que sa condition est vraie, exactement comme un if… sauf qu'à la fin du bloc, on remonte tester la condition à nouveau :

compteur = 1
while compteur <= 3:
    print(f"Tour {compteur}")
    compteur = compteur + 1
print("Terminé.")

Affiche Tour 1, Tour 2, Tour 3, puis Terminé. La variable compteur est initialisée avant la boucle, testée dans la condition, et modifiée à la fin du bloc. Si vous oubliez la modification, la condition reste vraie pour toujours : c'est la boucle infinie, le programme tourne sans s'arrêter — on l'interrompt avec Ctrl+C dans le terminal.

Deux commandes de pilotage dans une boucle : break sort de la boucle immédiatement, continue saute directement au tour suivant. Le motif classique du jour :

while True:
    reponse = input("Encore ? (oui/non) ")
    if reponse == "non":
        break
print("Au revoir.")

Une condition toujours vraie, et une sortie explicite par break : c'est la structure standard d'un programme qui dialogue jusqu'à ce qu'on lui dise d'arrêter.

Que se passe-t-il si on oublie compteur = compteur + 1 dans le premier exemple ?

  1. Boucle infinie : « Tour 1 » s'affiche sans fin
  2. Une erreur
  3. La boucle ne démarre pas
Réponse

La condition reste vraie pour toujours : la boucle ne s'arrête jamais. Ctrl+C pour l'interrompre.

Que fait break ?

  1. Il saute au tour suivant
  2. Il sort de la boucle immédiatement
  3. Il arrête tout le programme
Réponse

break quitte la boucle ; l'exécution continue après elle. (continue, lui, saute au tour suivant.)

Pratique (30 min)

  1. Retapez le premier exemple dans compte.py, lancez-le. Puis supprimez la ligne d'incrément, relancez, laissez « Tour 1 » défiler trois secondes, interrompez avec Ctrl+C : vous savez maintenant reconnaître et stopper une boucle infinie. Remettez la ligne.
  2. Retapez le deuxième exemple dans dialogue.py, lancez, répondez « oui » deux fois puis « non ».
  3. Le jeu du plus ou moins, dans mystere.py :
secret = 7
essais = 0
while True:
    guess = int(input("Devine le nombre (1-10) : "))
    essais = essais + 1
    if guess < secret:
        print("Plus grand !")
    elif guess > secret:
        print("Plus petit !")
    else:
        print(f"Gagné en {essais} essais !")
        break
  1. Critère de réussite : le jeu vous guide (« plus grand » / « plus petit ») jusqu'à la victoire, et annonce le nombre d'essais. Jouez deux parties.

Tips agent : « écris-moi un plus-ou-moins » est tentant ; le faire soi-même prend dix minutes et installe la structure while True + break pour toujours. Si vous déléguez quand même : piège classique, l'agent oublie int() autour d'input. Preuve : jouez une partie complète, pas juste un lancement.

Synthèse (10 min)

Trois lignes : les trois rôles de la variable de boucle (initier, tester, modifier), break vs continue, Ctrl+C. Sans notes : boucle qui affiche les carrés de 1 à 5 (1, 4, 9, 16, 25).

Jour 16 — La boucle for et range() : répéter un nombre connu de fois

Objectif : choisir entre for et while, et maîtriser range().

Théorie (20 min)

Quand on sait combien de fois on veut répéter — ou sur quoi on veut répéter — la boucle for est plus simple que while : pas de compteur à gérer, pas de risque de boucle infinie. range(n) produit les nombres de 0 à n-1 :

for i in range(5):
    print(i)

Affiche 0, 1, 2, 3, 4. Ça commence à 0 et ça s'arrête avant la fin : range(5) ne contient pas 5. C'est la convention de Python partout, et c'est l'erreur « décalage de un » (off-by-one) la plus fréquente de la programmation. Variantes : range(1, 6) donne 1 à 5 ; range(0, 10, 2) donne 0, 2, 4, 6, 8 (le troisième nombre est le pas).

Se prononce « pour chaque » : for i in range(5) se lit « pour chaque i dans 0, 1, 2, 3, 4 ». La variable (ici i) prend une nouvelle valeur à chaque tour, automatiquement.

for ou while ? Règle simple : nombre de tours connu à l'avance → for ; on répète jusqu'à ce que quelque chose arrive → while. Compter de 1 à 10 : for. Demander un mot de passe jusqu'à ce qu'il soit bon : while.

Combien de nombres produit range(4) ?

  1. 3
  2. 4
  3. 5
Réponse

4 : 0, 1, 2, 3. Ça commence à 0 et la borne de fin est exclue.

Pour afficher les tables de 3 jusqu'à 30, la meilleure boucle est…

  1. while, car on ne connaît pas la fin
  2. while True avec break
  3. for avec range(3, 31, 3)
Réponse

On connaît exactement les valeurs : for et son pas de 3.

Pratique (30 min)

  1. Au REPL : list(range(5)), list(range(1, 6)), list(range(10, 0, -1)) — list() rend visible ce que range produit. Notez le pas négatif du troisième.
  2. Créez tables.py :
table = int(input("Table de ? "))
for i in range(1, 11):
    print(f"{table} x {i} = {table * i}")
  1. Critère de réussite : avec 7, dix lignes s'affichent de « 7 x 1 = 7 » à « 7 x 10 = 70 ».
  2. Ajoutez avant la boucle total = 0 et dans la boucle total = total + i, puis un print final : vous venez d'écrire un accumulateur, le motif qui sert à tout totaliser (il reviendra aux jours 27 et 28).
  3. Réécrivez le jeu du plus ou moins d'hier avec for essai in range(5): — cinq essais maximum — et un message « Perdu ! » si la boucle finit sans break.

Tips agent : « convertis ce while en for » est une bonne refonte à déléguer. Piège : range(1, n) oublie le dernier — l'off-by-one est le bug que l'agent laisse passer le plus souvent. Preuve : comptez les lignes affichées, elles doivent être exactement au nombre attendu.

Synthèse (10 min)

Trois lignes : pourquoi range exclut la fin ne doit plus vous surprendre, for vs while, ce qu'est un accumulateur. Sans notes : for qui affiche la somme des nombres de 1 à 100 (vérifiez : 5050).

Jour 17 — Les listes : stocker plusieurs valeurs

Objectif : créer une liste, accéder à ses éléments par leur position, en extraire des tranches.

Théorie (20 min)

Une liste range plusieurs valeurs dans une seule variable, entre crochets, séparées par des virgules :

courses = ["pain", "lait", "œufs", "café"]
print(courses)

Chaque élément a une position, appelée index, et — c'est la même convention que range — le premier élément est à l'index 0. courses[0] vaut "pain", courses[1] vaut "lait". len(courses) donne le nombre d'éléments (4), donc le dernier index valide est len - 1 (3). Demander courses[4] provoque une IndexError: list index out of range — retenez ce message, vous le croiserez.

Index spécial : courses[-1] donne le dernier élément, courses[-2] l'avant-dernier. Pratique quand on ne connaît pas la longueur.

Les tranches (slices) extraient un morceau : courses[1:3] donne ["lait", "œufs"] — début inclus, fin exclue, comme range. courses[:2] les deux premiers, courses[2:] à partir du troisième.

Et la boucle for prend tout son sens : elle parcourt une liste directement, sans index :

for article in courses:
    print(f"- {article}")

Avec jours = ["lun", "mar", "mer"], que vaut jours[1] ?

  1. "lun"
  2. "mar"
  3. Une IndexError
Réponse

Les index commencent à 0 : jours[0] est "lun", jours[1] est "mar".

Comment obtenir le dernier élément d'une liste sans connaître sa longueur ?

  1. liste[-1]
  2. liste[len]
  3. liste[0]
Réponse

L'index -1 pointe toujours le dernier élément.

Pratique (30 min)

  1. Au REPL, créez jours = ["lun", "mar", "mer", "jeu", "ven", "sam", "dim"] et testez : jours[0], jours[-1], jours[5:] (le week-end !), jours[:5], len(jours). Puis jours[7] : lisez l'IndexError.
  2. Créez playlist.py :
morceaux = ["Song to Song", "Midnight Sun", "River", "Comptine"]
print(f"{len(morceaux)} morceaux :")
for m in morceaux:
    print(f"♪ {m}")
print(f"Prochaine écoute : {morceaux[0]}")
  1. Critère de réussite : le programme affiche le nombre de morceaux, la liste à puces, et le premier morceau — changez la liste, tout suit.
  2. Ajoutez print(morceaux[1:3]) et vérifiez que la fin est bien exclue.

Tips agent : « pourquoi liste[3] plante alors que ma liste a 3 éléments ? » : l'agent répond « les index commencent à 0 » en une ligne. Piège : il peut suggérer liste[len(liste)] qui plante aussi. Preuve : la dernière valeur affichée est bien le dernier élément de votre liste.

Synthèse (10 min)

Trois lignes : index 0, index -1, tranche début inclus / fin exclu. Sans notes : liste de trois films, afficher le deuxième et les deux derniers.

Jour 18 — Les listes : modifier, trier, parcourir

Objectif : ajouter, enlever, trier des éléments, et construire une liste dans une boucle.

Théorie (20 min)

Les listes se modifient après leur création — c'est ce qui les distingue des chaînes. Les méthodes du jour :

  • liste.append(x) ajoute x à la fin ;
  • liste.remove(x) enlève la première occurrence de x (erreur si absent) ;
  • liste.pop() enlève et rend le dernier élément ;
  • liste.sort() trie sur place (modifie la liste) ; sorted(liste) rend une nouvelle liste triée sans toucher l'originale ;
  • x in liste teste la présence (comme pour les chaînes au jour 8).

Le motif le plus utile de la programmation quotidienne : construire une liste dans une boucle. On part d'une liste vide, on remplit :

doubles = []
for n in range(1, 6):
    doubles.append(n * 2)
print(doubles)

Affiche [2, 4, 6, 8, 10]. Liste vide + boucle + append : ce squelette sert à filtrer, transformer, collecter — vous le réutiliserez dans le parseur CSV du jour 27.

Attention à un piège célèbre : b = a ne copie pas une liste, il colle une deuxième étiquette sur la même boîte. Modifier b modifie a. Pour copier vraiment : b = a.copy() ou b = a[:].

Après a = [1, 2], b = a, b.append(3), que vaut a ?

  1. [1, 2]
  2. Une erreur
  3. [1, 2, 3]
Réponse

b = a nomme la même liste deux fois : append sur b est visible depuis a. Pour copier : a.copy().

Différence entre liste.sort() et sorted(liste) ?

  1. sort() modifie la liste, sorted() en rend une nouvelle
  2. Aucune, ce sont deux noms pour la même chose
  3. sorted() trie à l'envers
Réponse

sort() travaille sur place et rend None ; sorted() laisse l'originale intacte.

Pratique (30 min)

  1. Créez scores.py :
scores = []
while True:
    entree = input("Un score (ou 'fin') ? ")
    if entree == "fin":
        break
    scores.append(int(entree))
scores.sort()
print(f"Scores : {scores}")
print(f"Meilleur : {scores[-1]}")
  1. Critère de réussite : entrez 12, 4, 27, 8 puis « fin » : la liste s'affiche triée et le meilleur score est 27 — collecte, tri et lecture du dernier élément en un programme.
  2. Reproduisez le piège de l'alias : a = [1, 2], b = a, b.append(3), affichez a. Puis refaites avec b = a.copy() et constatez la différence.
  3. Ajoutez au programme : après le tri, scores.remove(min(scores)) puis réaffichez — min() et max() donnent le plus petit / grand élément.

Tips agent : « mon append sur b modifie aussi a, bug de Python ? » : l'agent explique l'aliasing. Piège : il propose parfois copy.deepcopy, inutile pour une liste simple. Preuve : un print de l'originale après modification de la copie.

Synthèse (10 min)

Trois lignes : append vs remove vs pop, sort vs sorted, pourquoi b = a ne copie pas. Sans notes : boucle qui construit la liste des carrés de 1 à 10, puis affiche son dernier élément.

Jour 19 — Les méthodes de chaînes : découper, nettoyer, recoller

Objectif : nettoyer et découper du texte réel : espaces, casse, séparateurs.

Théorie (20 min)

Les fichiers réels sont sales : espaces en trop, majuscules aléatoires, champs collés par des virgules. Quatre méthodes de chaînes font 90 % du nettoyage :

  • .strip() enlève espaces et retours à la ligne aux deux bords : " pain \n".strip() → "pain" ;
  • .lower() / .upper() normalisent la casse avant de comparer ;
  • .split(",") découpe en liste à chaque séparateur : "a,b,c".split(",") → ["a", "b", "c"]. Sans argument, .split() découpe sur les espaces ;
  • ", ".join(liste) fait l'inverse : recolle une liste en une chaîne : ", ".join(["a", "b"]) → "a, b".

Deux propriétés à connaître : une chaîne ne se modifie pas (on dit qu'elle est immuable) — mot.upper() ne change pas mot, il rend une nouvelle chaîne qu'il faut ranger : mot = mot.upper(). Et une chaîne se parcourt comme une liste : for lettre in "abc" tourne trois fois ; "abc"[0] vaut "a".

Utile aussi : .startswith("http") et .endswith(".pdf") testent le début et la fin — ils serviront au trieur de fichiers du module 3. Et .replace("a", "b") remplace toutes les occurrences.

"Jean,25,Lyon".split(",") donne…

  1. La chaîne "Jean 25 Lyon"
  2. La liste ["Jean", "25", "Lyon"]
  3. La liste ["Jean,25,Lyon"]
Réponse

split découpe à chaque virgule et rend une liste de trois chaînes — le "25" reste du texte, à convertir avec int() si besoin.

Après mot = "abc" puis mot.upper(), que vaut mot ?

  1. "abc" — upper() rend une nouvelle chaîne sans modifier mot
  2. "ABC"
  3. Une erreur
Réponse

Les chaînes sont immuables : il faut ranger le résultat, mot = mot.upper().

Pratique (30 min)

  1. Au REPL : " Alice ".strip(), "a b c".split(), "-".join(["2026", "11", "03"]), "photo.pdf".endswith(".pdf").
  2. Créez nettoyage.py :
ligne_sale = "  DUPONT, alice , LYON  "
morceaux = ligne_sale.split(",")
propres = []
for m in morceaux:
    propres.append(m.strip().title())
print(propres)
print(" | ".join(propres))
  1. Critère de réussite : le programme affiche ['Dupont', 'Alice', 'Lyon'] puis Dupont | Alice | Lyon — la chaîne sale est devenue trois champs propres (.title() met la première lettre en majuscule).
  2. Changez la ligne sale (autres espaces, autre casse) : le résultat reste propre. C'est ça, un programme robuste.
  3. Demandez la ligne à l'utilisateur avec input() au lieu de l'écrire en dur.

Tips agent : « quelles méthodes de str pour nettoyer X » : l'agent liste bien. Piège : il invente des méthodes qui n'existent pas (.trim() n'existe pas en Python — c'est du JavaScript). Preuve : lancez chaque méthode proposée au REPL avant de l'adopter.

Synthèse (10 min)

Trois lignes : strip/split/join, l'immuabilité des chaînes, startswith/endswith. Sans notes : transformer " dupont ; alice " en "Dupont / Alice".

Jour 20 — Les tuples et le déballage

Objectif : connaître le cousin immuable de la liste, et déballer plusieurs valeurs d'un coup.

Théorie (20 min)

Un tuple (« n-uplet ») est comme une liste, mais figée : on l'écrit entre parenthèses, et une fois créé on ne peut ni ajouter, ni enlever, ni changer un élément :

point = (3, 4)
print(point[0])

À quoi ça sert, une liste qu'on ne peut pas modifier ? À dire « ces valeurs vont ensemble et ne bougeront pas » : des coordonnées, une date, une couleur (rouge, vert, bleu). Python s'en sert partout en interne, et vous les croiserez dans les bibliothèques.

La fonctionnalité qui rend les tuples addictifs est le déballage (unpacking) : distribuer les éléments dans plusieurs variables d'un coup :

largeur, hauteur = (1920, 1080)
print(largeur)

Ça marche aussi sans parenthèses — c'est même le style habituel : x, y = 3, 4. Et ça permet l'échange en une ligne, impossible dans la plupart des langages : a, b = b, a.

Le déballage s'applique aussi aux listes et aux retours de split : nom, age = "Alice,30".split(",") range "Alice" dans nom et "30" dans age — attention, il faut exactement le bon nombre d'éléments, sinon ValueError. Ce raccourci servira au jour 27 pour le CSV.

Différence essentielle entre tuple et liste ?

  1. Le tuple ne se modifie pas après création
  2. Le tuple est plus rapide à taper
  3. La liste ne contient que des nombres
Réponse

L'immuabilité : pas de append ni d'affectation point[0] = 5 sur un tuple.

Après a, b = 1, 2 puis a, b = b, a, que vaut a ?

  1. 1
  2. 2
  3. Une erreur
Réponse

Le déballage évalue d'abord tout le côté droit : l'échange fonctionne en une ligne.

Pratique (30 min)

  1. Au REPL : couleur = (255, 128, 0), couleur[1], puis couleur[0] = 100 : lisez la TypeError qui confirme l'immuabilité. Puis r, g, b = couleur et affichez g.
  2. Écrivez l'échange a, b = b, a au REPL et vérifiez.
  3. Créez annuaire.py :
contacts = [("Alice", "0601"), ("Bob", "0602"), ("Chloé", "0603")]
for nom, tel in contacts:
    print(f"{nom} : {tel}")
  1. Critère de réussite : trois lignes « nom : numéro » s'affichent — la boucle déballe chaque tuple directement dans nom et tel, sans index.
  2. Ajoutez un quatrième contact et relancez.
  3. Essayez nom, tel, extra = contacts[0] : lisez le ValueError « not enough values to unpack », notez-le au carnet.

Tips agent : « transforme ces parcours par index en déballage » — un agent modernise ça très bien. Piège : il peut casser l'arité (déballer 3 valeurs d'un tuple de 2). Preuve : relancez sur vos données, le moindre désaccord est une ValueError visible.

Synthèse (10 min)

Trois lignes : tuple vs liste, déballage, l'échange en une ligne. Sans notes : liste de trois tuples (ville, population), boucle qui affiche « ville — population ».

Jour 21 — Les dictionnaires : des paires clé-valeur

Objectif : stocker des données nommées et y accéder par leur nom.

Théorie (20 min)

Une liste retrouve ses éléments par leur position. Un dictionnaire les retrouve par un nom qu'on choisit — la clé. On l'écrit entre accolades, avec des paires clé: valeur :

alice = {"age": 30, "ville": "Lyon", "metier": "infirmière"}
print(alice["ville"])

alice["ville"] se lit « dans alice, la valeur rangée sous la clé "ville" ». Comme un vrai dictionnaire : on cherche un mot (la clé), on trouve sa définition (la valeur). Les clés sont des chaînes le plus souvent ; les valeurs peuvent être n'importe quoi — nombres, chaînes, listes, autres dictionnaires.

Créer, lire, modifier, ajouter, supprimer :

  • ajouter ou modifier : alice["age"] = 31 (si la clé existe, on remplace ; sinon on crée) ;
  • supprimer : del alice["metier"] ;
  • tester : "ville" in alice → True ;
  • lire une clé absente, alice["pseudo"] → KeyError, la sixième erreur de votre carnet.

Quand choisir un dictionnaire plutôt qu'une liste ? Liste : une collection d'éléments du même genre, dont l'ordre compte (des scores, des fichiers). Dictionnaire : une fiche avec des champs nommés (une personne, un produit, une configuration). La plupart des données du monde réel sont des listes de dictionnaires : un tableau, où chaque ligne est une fiche.

{"a": 1}["b"] provoque…

  1. IndexError
  2. TypeError
  3. KeyError
Réponse

La clé "b" n'existe pas : KeyError. (On verra .get() demain pour l'éviter.)

Comment ajouter la clé "email" à un dictionnaire d ?

  1. d.append("email", "...")
  2. d["email"] = "..."
  3. d.add("email", "...")
Réponse

On affecte à la clé : d["email"] = valeur crée ou remplace.

Pratique (30 min)

  1. Au REPL, créez le dictionnaire alice du cours, lisez alice["age"], modifiez-le, ajoutez alice["ville"] = "Paris" puis une clé alice["surnom"] avec la valeur de votre choix. Testez "ville" in alice, puis lisez une clé absente pour voir la KeyError.
  2. Créez inventaire.py :
stock = {"pommes": 12, "bananes": 5, "oranges": 0}
fruit = input("Quel fruit ? ").lower()
if fruit in stock:
    print(f"Il reste {stock[fruit]} {fruit}.")
else:
    print("Pas en stock.")
  1. Critère de réussite : « pommes » affiche 12, « kiwis » affiche « Pas en stock. » — pas de KeyError, grâce au test in.
  2. Ajoutez après la lecture : stock[fruit] = stock[fruit] - 1 dans la branche « en stock », affichez le nouveau stock complet.

Tips agent : « voici ma structure de données, liste ou dict ? » : l'agent tranche bien avec un exemple concret. Piège : il répond « ça dépend » si vous ne donnez pas vos données réelles — collez-en trois lignes. Preuve : votre accès aux données tient en une ligne lisible.

Synthèse (10 min)

Trois lignes : clé vs index, créer/modifier/supprimer, quand préférer un dict. Sans notes : dict « produit → prix », programme qui demande un produit et affiche son prix ou « inconnu ».

Jour 22 — Dictionnaires : parcourir, get, compter

Objectif : parcourir un dictionnaire proprement et compter des occurrences, le motif le plus utile du parcours.

Théorie (20 min)

Parcourir un dictionnaire : trois méthodes. d.keys() les clés, d.values() les valeurs, d.items() les deux en tuples — et c'est celle qu'on utilise tout le temps, avec le déballage du jour 20 :

stock = {"pommes": 12, "bananes": 5}
for fruit, quantite in stock.items():
    print(f"{fruit} : {quantite}")

Deuxième outil : .get(cle, defaut) lit une clé sans risque — si elle est absente, il rend le défaut au lieu de lever une KeyError :

print(stock.get("kiwis", 0))

Affiche 0 sans planter. Avec ces deux outils, voici le motif compteur, probablement le plus utile du parcours entier — compter les occurrences de n'importe quoi :

mots = ["chat", "chien", "chat", "oiseau", "chat"]
compte = {}
for mot in mots:
    compte[mot] = compte.get(mot, 0) + 1
print(compte)

Affiche {'chat': 3, 'chien': 1, 'oiseau': 1}. À chaque mot : « prends le compte actuel (0 si jamais vu), ajoute 1, range ». Retenez ce squelette de trois lignes : il servira pour les dépenses du jour 27, les extensions de fichiers du module 3, et la moitié des traitements de données qui existent.

Que rend {"a": 1}.get("b", 0) ?

  1. 0
  2. Une KeyError
  3. None
Réponse

get avec un défaut ne plante jamais : il rend le défaut quand la clé manque.

Pour avoir clé ET valeur dans une boucle, on écrit…

  1. for k, v in d:
  2. for k, v in d.items():
  3. for k, v in d.keys():
Réponse

items() rend les paires, déballées en deux variables.

Pratique (30 min)

  1. Retapez le compteur du cours dans compteur.py, lancez, vérifiez le résultat.
  2. Critère de réussite : le dictionnaire affiché donne les bonnes fréquences ; ajoutez un mot à la liste, relancez, le compte suit.
  3. Variante : comptez les lettres d'une phrase tapée par l'utilisateur (for lettre in phrase.lower():, ignorez les espaces avec un if).
  4. Créez facture.py : un dict prix = {"pain": 1.2, "lait": 1.5, "café": 4.9}, une liste panier = ["pain", "pain", "café"], et un accumulateur qui calcule le total en consultant le dict. Affichez Total : 7.30 € avec une f-string :.2f.

Tips agent : « compte les occurrences de X dans cette liste » : l'agent sortira peut-être collections.Counter — ça existe et c'est très bien, mais écrire le compteur à la main d'abord est l'exercice. Piège : accepter Counter sans comprendre le motif get(). Preuve : réécrivez la version manuelle et comparez les deux sorties.

Synthèse (10 min)

Trois lignes : items() + déballage, get vs KeyError, le motif compteur en trois lignes. Sans notes : compter les voyelles de « programmation ».

Jour 23 — Les ensembles : l'unicité gratuite

Objectif : éliminer les doublons et faire des opérations d'ensemble (union, intersection).

Théorie (20 min)

Un ensemble (set) est une collection sans doublons et sans ordre : chaque valeur n'y existe qu'une fois. On en crée un avec des accolades (comme un dict, mais sans les deux-points) ou à partir d'une liste :

invites = ["Alice", "Bob", "Alice", "Chloé", "Bob"]
uniques = set(invites)
print(uniques)
print(len(uniques))

Affiche un ensemble de trois noms — les doublons ont disparu sans effort. C'est l'usage n°1 du set : dédupliquer. Attention : un set ne garantit pas l'ordre ; si l'ordre compte, on repasse par une liste.

Les opérations de la théorie des ensembles, telles qu'apprises à l'école :

  • union (|) : tout ce qui est dans l'un ou l'autre ;
  • intersection (&) : ce qui est dans les deux ;
  • différence (-) : ce qui est dans le premier mais pas le second.
a = {"Alice", "Bob", "Chloé"}
b = {"Bob", "David"}
print(a & b)
print(a - b)

Affiche {'Bob'} puis {'Alice', 'Chloé'}. Et le test d'appartenance x in ensemble est ultra-rapide, bien plus que sur une liste — utile quand on vérifie des milliers de valeurs (jour 41).

Que vaut len(set([1, 2, 2, 3, 3, 3])) ?

  1. 6
  2. Une erreur
  3. 3
Réponse

Le set ne garde que les valeurs distinctes : 1, 2, 3.

Qu'est-ce qu'un set ne garantit PAS ?

  1. L'ordre des éléments
  2. L'absence de doublons
  3. Le test d'appartenance avec in
Réponse

Un set est sans ordre : ne comptez pas sur l'ordre d'affichage.

Pratique (30 min)

  1. Au REPL : set("mississippi") — les lettres distinctes d'un coup. Puis len(set("mississippi")).
  2. Créez soirees.py :
vendredi = {"Alice", "Bob", "Chloé", "David"}
samedi = {"Bob", "David", "Emma"}
print(f"Les deux soirs : {vendredi & samedi}")
print(f"Uniquement vendredi : {vendredi - samedi}")
print(f"Au moins un soir : {vendredi | samedi}")
print(f"Total de têtes : {len(vendredi | samedi)}")
  1. Critère de réussite : les trois ensembles affichés correspondent à votre calcul à la main (vérifiez chacun sur le papier), et le total vaut 5.
  2. Exercice réaliste : une liste de 10 mots avec doublons tapée en dur ; affichez « 10 mots tapés, 7 distincts » (adaptez au vrai compte) avec len() et len(set()).

Tips agent : « j'ai des doublons dans ma liste » → l'agent répond set() en une ligne. Piège : il oublie de prévenir que l'ordre saute. Preuve : affichez avant/après et vérifiez que le nombre d'éléments distincts est bon.

Synthèse (10 min)

Trois lignes : à quoi sert un set, les trois opérations, le piège de l'ordre. Sans notes : intersection de deux listes de courses de la semaine.

Jour 24 — Lire un fichier texte

Objectif : ouvrir un fichier, lire son contenu ligne par ligne, et toujours le refermer proprement.

Théorie (20 min)

Jusqu'ici, vos données mouraient à la fin du programme. Les fichiers les font survivre : ce sont les boîtes où l'ordinateur garde les données entre deux exécutions. Lire un fichier texte en Python tient en trois lignes, avec le mot-clé with :

with open("notes.txt", encoding="utf-8") as f:
    for ligne in f:
        print(ligne.strip())

Décortiquons. open() ouvre le fichier et rend un objet-fichier, ici rangé dans f. encoding="utf-8" dit comment les caractères sont codés en octets — utf-8 est le standard universel ; sans lui, vos accents peuvent sortir en hiéroglyphes sur une autre machine. with ... as ... garantit que le fichier sera refermé à la fin du bloc indenté, même en cas d'erreur : toujours ouvrir avec with. Enfin, un fichier se parcourt ligne par ligne comme une liste — et chaque ligne garde son retour à la ligne final (\n), d'où le .strip() du jour 19.

Si le fichier n'existe pas : FileNotFoundError. Deux réflexes : vérifier que le terminal est dans le bon dossier (cd, jour 2), et vérifier l'orthographe exacte du nom.

Variantes utiles : f.read() lit tout le fichier d'un coup en une seule chaîne ; f.readlines() rend la liste des lignes. Pour les gros fichiers, restez sur la boucle ligne par ligne : elle ne charge jamais tout en mémoire.

Pourquoi ouvrir un fichier avec with ?

  1. C'est plus rapide
  2. Le fichier est refermé automatiquement, même si une erreur survient
  3. Ça évite l'encoding
Réponse

with garantit la fermeture quoi qu'il arrive dans le bloc.

Pourquoi chaque ligne lue finit-elle par un affichage en trop avec print ?

  1. Chaque ligne contient son \n final, et print en ajoute un — d'où le .strip()
  2. Python lit les lignes deux fois
  3. Le fichier est mal encodé
Réponse

Le \n de la ligne plus celui de print font une ligne vide : strip() nettoie.

Pratique (30 min)

  1. Créez à la main, dans votre éditeur, un fichier depenses.txt :
pain 1.20
cinéma 9.50
livre 14.00
café 2.10
  1. Créez lire.py dans le même dossier :
total = 0.0
with open("depenses.txt", encoding="utf-8") as f:
    for ligne in f:
        nom, montant = ligne.strip().split()
        total = total + float(montant)
        print(f"{nom} : {montant} €")
print(f"Total : {total:.2f} €")
  1. Critère de réussite : quatre lignes s'affichent, puis Total : 26.80 € — votre programme lit un vrai fichier, déballe chaque ligne en deux variables (jour 20 !) et accumule un total (jour 16 !). Tout se recoupe.
  2. Renommez le fichier et relancez : lisez la FileNotFoundError, vérifiez le nom indiqué, réparez.
  3. Changez un montant dans le fichier texte (pas dans le programme !), relancez : le total suit. Vos données vivent désormais hors du code.

Tips agent : « lis ce fichier et fais la somme de la 2e colonne » : l'agent écrit ça vite. Pièges : il oublie l'encoding, ou suppose un séparateur (espace vs tabulation) sans regarder le fichier. Preuve : collez-lui trois lignes réelles du fichier et exigez le total, que vous vérifiez à la main.

Synthèse (10 min)

Trois lignes : with + open + encoding, pourquoi strip chaque ligne, FileNotFoundError et ses deux causes. Sans notes : lire un fichier de cinq lignes et afficher la plus longue.

Jour 25 — Écrire dans un fichier

Objectif : créer et modifier des fichiers depuis un programme : modes d'ouverture et retours à la ligne.

Théorie (20 min)

open() prend un deuxième argument : le mode.

  • "r" : lecture (read) — le défaut, celui d'hier ;
  • "w" : écriture (write) — crée le fichier s'il n'existe pas, et efface son contenu s'il existe. Le piège du jour : ouvrir en "w" un fichier qu'on voulait garder, c'est le vider instantanément ;
  • "a" : ajout (append) — écrit à la fin sans toucher l'existant.
with open("journal.txt", "w", encoding="utf-8") as f:
    f.write("Première ligne\n")
    f.write("Deuxième ligne\n")

Deux différences avec print : f.write() n'ajoute pas de retour à la ligne — il faut écrire le \n soi-même (c'est le caractère invisible « nouvelle ligne ») ; et write n'accepte que des chaînes : convertissez les nombres avec une f-string ou str().

Le mode "a" est parfait pour un journal :

with open("journal.txt", "a", encoding="utf-8") as f:
    f.write("Une ligne de plus\n")

Relancez ce programme trois fois : le fichier grandit de trois lignes. Avec "w", il n'aurait toujours qu'une ligne. Retenez la règle : "w" repart de zéro, "a" continue l'histoire.

Que fait open("x.txt", "w") si x.txt existe déjà avec du contenu ?

  1. Il ajoute à la fin
  2. Il vide le fichier avant écriture
  3. Il refuse et lève une erreur
Réponse

Le mode w écrase : le contenu précédent est perdu. Pour ajouter : mode "a".

Pourquoi f.write(42) plante-t-il ?

  1. write n'accepte que des chaînes : écrire f.write("42") ou f.write(f"{n}")
  2. Il manque le mode "w"
  3. Il manque le \n
Réponse

TypeError : convertir en texte d'abord.

Pratique (30 min)

  1. Créez memo.py :
note = input("Ta note du jour ? ")
with open("memo.txt", "a", encoding="utf-8") as f:
    f.write(note + "\n")
print("Noté.")
  1. Lancez-le trois fois avec trois notes différentes. Ouvrez memo.txt dans l'éditeur : critère de réussite, les trois notes y sont, une par ligne.
  2. Changez "a" en "w", relancez deux fois : il ne reste que la dernière note. Voilà la différence, vue de vos yeux. Remettez "a".
  3. Ajoutez la lecture : un deuxième bloc with open("memo.txt", encoding="utf-8") as f: qui réaffiche toutes les notes après l'ajout.
  4. Écrivez un script qui recopie depenses.txt d'hier vers depenses-avec-tva.txt en multipliant chaque montant par 1.2 (lire + écrire : l'exercice complet).

Tips agent : « ajoute une ligne à la fin de ce fichier » : l'agent utilise "a" à juste titre. Piège : quand il « améliore » un script, il peut remplacer un "a" par un "w" sans prévenir — et vos données sautent au prochain run. Preuve : lancez deux fois et comptez les lignes du fichier.

Synthèse (10 min)

Trois lignes : les trois modes, le \n explicite, write n'accepte que du texte. Sans notes : programme qui demande trois prénoms et les écrit dans un fichier, un par ligne.

Jour 26 — try / except : attraper les erreurs

Objectif : empêcher le programme de planter sur une erreur prévisible, et réagir proprement.

Théorie (20 min)

Vous savez lire les erreurs ; apprenez à les attraper. Certaines erreurs ne sont pas des bugs : elles sont prévisibles — l'utilisateur tape « abc » quand on attend un nombre, le fichier n'existe pas encore au premier lancement. Le bloc try / except dit : « essaie ceci ; si telle erreur arrive, fais cela au lieu de planter » :

entree = input("Un nombre ? ")
try:
    n = int(entree)
    print(f"Le double : {n * 2}")
except ValueError:
    print(f"'{entree}' n'est pas un nombre.")

Python exécute le bloc try ; si une ValueError y survient, il saute immédiatement au bloc except correspondant, et le programme continue au lieu de s'arrêter. Le reste du programme après le try/except s'exécute normalement.

Deux règles d'hygiène, valables toute votre vie de codeur :

  • Attrapez l'erreur précise (ValueError, FileNotFoundError…), jamais un except: nu qui avale tout — il cacherait aussi vos vrais bugs, et un programme qui cache ses bugs est pire qu'un programme qui plante.
  • Le bloc try reste petit : uniquement la ou les lignes qui peuvent lever l'erreur attendue. Plus il est gros, moins vous savez quelle ligne a déclenché le except.

Combiné avec while, try/except donne la saisie robuste : redemander jusqu'à obtenir une valeur valide.

Pourquoi éviter except: sans nom d'erreur ?

  1. C'est plus lent
  2. Il masque aussi les bugs imprévus, qui deviennent introuvables
  3. C'est interdit par Python
Réponse

Un except nu avale tout, y compris vos fautes de frappe : le programme continue avec des résultats faux au lieu de planter.

int("douze") dans un try : quel except l'attrape ?

  1. except TypeError
  2. except NameError
  3. except ValueError
Réponse

La valeur est du bon type (chaîne) mais non convertible : ValueError.

Pratique (30 min)

  1. Retapez l'exemple du cours dans robuste.py, testez avec 21 puis avec « abc » : les deux cas répondent sans planter.
  2. Saisie forcée :
while True:
    entree = input("Ton âge ? ")
    try:
        age = int(entree)
        break
    except ValueError:
        print("En chiffres, s'il te plaît.")
print(f"Merci, {age} ans.")
  1. Critère de réussite : tapez « abc », puis « 12.5 », puis « 25 » : le programme redemande poliment tant que ce n'est pas un entier, puis remercie. Aucune erreur visible, jamais.
  2. Repensez au fichier du jour 24 : ajoutez try: ... except FileNotFoundError: autour du open() pour afficher « crée d'abord depenses.txt » au lieu de planter.

Tips agent : « rends cette saisie robuste » : l'agent écrit le while/try/except correctement. Piège : il met parfois le break au mauvais endroit et boucle à l'infini, ou attrape trop large. Preuve : testez trois entrées invalides de suite, puis une valide.

Synthèse (10 min)

Trois lignes : ce que fait try/except, pourquoi nommer l'erreur, pourquoi le bloc try reste petit. Sans notes : demander un float jusqu'à en obtenir un.

Jour 27 — Découper un CSV à la main

Objectif : lire un fichier CSV ligne par ligne, découper les champs, et produire un résumé chiffré.

Théorie (20 min)

Le format CSV (comma-separated values, « valeurs séparées par des virgules ») est le format d'échange le plus courant du monde : un tableau en texte brut, une ligne par enregistrement, les colonnes séparées par des virgules (ou des points-virgules chez les tableurs français). Excel, LibreOffice, votre banque, exportent tous du CSV :

date,libellé,montant
2026-10-01,courses,-42.50
2026-10-02,salaire,2100.00
2026-10-03,cinéma,-9.50

La première ligne est l'en-tête : elle nomme les colonnes, elle ne se traite pas comme une donnée. Pour la sauter, on lit la première ligne avant la boucle avec next(f).

Tout ce qu'il faut, vous l'avez déjà : la boucle ligne par ligne (jour 24), strip et split (jour 19), le déballage (jour 20), la conversion float (jour 6), l'accumulateur (jour 16) et le motif compteur (jour 22). On assemble :

total = 0.0
with open("compte.csv", encoding="utf-8") as f:
    next(f)
    for ligne in f:
        date, libelle, montant = ligne.strip().split(",")
        total = total + float(montant)
print(f"Solde : {total:.2f} €")

Limite honnête du découpage à la main : si un champ contient lui-même une virgule entre guillemets ("resto, Le Petit",-23.00), split(",") coupe au mauvais endroit. Le vrai module csv de Python gère ce cas — ce sera le jour 50. Pour vos propres fichiers sans virgules internes, la méthode manuelle suffit.

Pourquoi next(f) avant la boucle ?

  1. Pour lire et ignorer la ligne d'en-tête
  2. Pour ouvrir le fichier
  3. Pour compter les lignes
Réponse

L'en-tête nomme les colonnes ; next() la consomme pour que la boucle ne voie que les données.

Cas où split(",") échoue sur un CSV ?

  1. Quand le fichier est en utf-8
  2. Quand un champ contient une virgule entre guillemets
  3. Quand il y a plus de trois colonnes
Réponse

split coupe à TOUTES les virgules, y compris celles internes à un champ. Le module csv (jour 50) gère les guillemets.

Pratique (30 min)

  1. Créez compte.csv avec le contenu du cours, plus trois lignes de votre invention (une recette positive, deux dépenses négatives).
  2. Retapez le programme du cours dans solde.py, lancez-le, vérifiez le solde à la calculatrice.
  3. Critère de réussite : le solde affiché égale votre calcul manuel au centime près.
  4. Ajoutez deux accumulateurs : depenses (somme des montants négatifs) et recettes (somme des positifs), avec un if sur le signe du montant, et affichez les trois lignes.
  5. Ajoutez une ligne avec une virgule dans le libellé entre guillemets, relancez : le programme affiche une ValueError ou un solde faux. C'est fait exprès : vous venez de toucher la limite du découpage manuel. Enlevez la ligne, notez la limite au carnet.

Tips agent : « résume ce CSV par colonne » est la tâche d'agent par excellence. Pièges : séparateur supposé (, vs ;), en-tête oublié, montants lus comme du texte. Preuve : exigez trois nombres (recettes, dépenses, solde) et vérifiez-les à la main sur cinq lignes.

Synthèse (10 min)

Trois lignes : la structure d'un CSV, next() pour l'en-tête, la limite du split manuel. Sans notes : CSV prénom,âge de quatre lignes, programme qui affiche l'âge moyen.

Jour 28 — Bilan du module 2 : le carnet d'adresses

Objectif : assembler boucles, listes, dicts, fichiers et try/except dans un programme complet à deux fonctions.

Théorie (20 min)

Rien de neuf : on assemble, et on prend le temps de concevoir avant de coder. La conception d'un petit programme tient en trois questions, à écrire dans le carnet avant la première ligne de code :

  1. Quelles données ? Ici : des contacts (nom, téléphone). Structure : une liste de chaînes "nom;tel" dans un fichier, et en mémoire une liste de tuples déballés.
  2. Quelles actions ? Deux : « ajouter un contact » et « lister les contacts ».
  3. Quel squelette ? Une boucle while True qui affiche un menu (jour 15), un if/elif sur le choix, les fichiers pour la persistance (jours 24-25), try/except pour les saisies (jour 26).

Un mot sur les séparateurs : on choisit ; plutôt que , pour les contacts, parce que les noms peuvent contenir des virgules (« Martin, Jean ») mais rarement des points-virgules. Choisir son format de fichier, c'est déjà programmer.

Pratique (30 min)

Le programme bilan, à écrire seul (le squelette vous est donné, remplissez les ...) :

FICHIER = "contacts.txt"

def afficher_menu():
    print("1. Ajouter un contact")
    print("2. Lister les contacts")
    print("3. Quitter")

while True:
    afficher_menu()
    choix = input("Choix ? ")
    if choix == "1":
        nom = input("Nom ? ").strip()
        tel = input("Téléphone ? ").strip()
        with open(FICHIER, "a", encoding="utf-8") as f:
            f.write(f"{nom};{tel}\n")
        print("Ajouté.")
    elif choix == "2":
        try:
            with open(FICHIER, encoding="utf-8") as f:
                for ligne in f:
                    nom, tel = ligne.strip().split(";")
                    print(f"{nom} : {tel}")
        except FileNotFoundError:
            print("Aucun contact pour l'instant.")
    elif choix == "3":
        break
    else:
        print("Choix inconnu.")

La ligne def afficher_menu(): est un avant-goût du jour 29 : un bloc nommé qu'on appelle par son nom. Tapez-la comme elle est, elle marche.

  1. Critère de réussite : ajoutez trois contacts, listez-les, quittez, relancez le programme, listez à nouveau : les trois contacts ont survécu à l'arrêt du programme — c'est la persistance, la vraie nouveauté de ce module.
  2. Testez « lister » après avoir supprimé contacts.txt : le message poli s'affiche au lieu du plantage.
  3. Amélioration : refusez un nom vide (if nom == "" → message, pas d'écriture).

Tips agent : ce programme, un agent le produit d'un bloc si vous décrivez les trois questions de conception. Faites-le à la main d'abord, puis comparez avec sa version. Piège : la sienne utilisera peut-être des techniques que vous ne pouvez pas encore relire (json, classes). Preuve : vous devez pouvoir expliquer chaque ligne, sinon ce n'est pas votre outil.

Synthèse (10 min)

Trois lignes : les trois questions de conception, pourquoi le « ; » ici, ce qu'est la persistance. Module 3 demain : les fonctions, pour de vrai.

Utile ? Dites-nous ce qu'il faut mesurer ensuite.

Commentaires

Ni compte ni traceur : un commentaire est lu par un humain avant d'apparaître.