Du script au projet structuré

Écrire des programmes Python lisibles, du premier print() au projet testé : syntaxe, fonctions, POO, collections, fichiers, gestion d'erreurs et tests avec la bibliothèque standard.

  1. Public cible

    Débutants, étudiants et professionnels qui souhaitent acquérir une pratique structurée dans ce domaine.

  2. Objectifs

    Comprendre les notions essentielles et les appliquer dans un projet vérifiable.

  3. Prérequis

    Aucune connaissance préalable n’est strictement obligatoire ; les prérequis détaillés sont précisés dans les ateliers.

  4. Niveau

    Tous niveaux

  5. Ressources

    • Documentation officielle et références citées dans les ateliers.
    • Exercices, examens et projet de synthèse du catalogue.
  6. Validation des compétences

    La validation repose sur les évaluations actives et les livrables pratiques du cours.

  7. Besoins techniques

    Un ordinateur, un navigateur et les outils indiqués dans le premier atelier sont suffisants pour commencer.

Exercices, examens et projets

Découvrir Python et l'environnement

Exercice

Atelier Python

Écrire et tester des fonctions Python en respectant PEP 8.

Obligatoire Consulter
Examen

Examen Python

Évaluer syntaxe, structures, POO, exceptions et tests.

Obligatoire Consulter

Syntaxe, types et structures de contrôle

Exercice

Atelier Python

Écrire et tester des fonctions Python en respectant PEP 8.

Obligatoire Consulter
Examen

Examen Python

Évaluer syntaxe, structures, POO, exceptions et tests.

Obligatoire Consulter

Fonctions, modules et POO

Exercice

Atelier Python

Écrire et tester des fonctions Python en respectant PEP 8.

Obligatoire Consulter
Examen

Examen Python

Évaluer syntaxe, structures, POO, exceptions et tests.

Obligatoire Consulter

Collections, itertools et fichiers

Exercice

Atelier Python

Écrire et tester des fonctions Python en respectant PEP 8.

Obligatoire Consulter
Examen

Examen Python

Évaluer syntaxe, structures, POO, exceptions et tests.

Obligatoire Consulter

Erreurs, exceptions et tests

Exercice

Atelier Python

Écrire et tester des fonctions Python en respectant PEP 8.

Obligatoire Consulter
Examen

Examen Python

Évaluer syntaxe, structures, POO, exceptions et tests.

Obligatoire Consulter

Projet final et méthodologie

Exercice

Atelier Python

Écrire et tester des fonctions Python en respectant PEP 8.

Obligatoire Consulter
Examen

Examen Python

Évaluer syntaxe, structures, POO, exceptions et tests.

Obligatoire Consulter
  1. Découvrir Python et l'environnement

    1. Présentation du cours

      Objectifs

      1. Écrire des programmes Python lisibles et exécutables.
      2. Maîtriser les types, les structures de contrôle et les fonctions.
      3. Utiliser les collections et la bibliothèque standard plutôt que réécrire à la main.
      4. Gérer les erreurs proprement au lieu de laisser la trace Python planter.
      5. Écrire des tests et organiser un projet en modules.

      Public visé

      Débutants en programmation et developers venant d'un autre langage qui oublient les réflexes propres à Python. Aucun prérequis en algorithmique n'est demandé, seulement l'envie de s'entraîner.

      Durée

      40 heures, en modules courts. Chaque module se termine par des exercices corrigés et un atelier.

      Prérequis techniques

      • Un ordinateur sous Windows, macOS ou Linux.
      • Un éditeur de texte : VS Code est confortable mais n'est pas obligatoire.
      • Aucune installation de Python n'est nécessaire pour la première séance.
    2. Le langage en bref

      Python est un langage interprété, à indentation significative et à typage dynamique. L'indentation ne sert pas qu'à la lisibilité : elle détermine les blocs.

      Versions actives

      D'après la page officielle de téléchargement (relevée le 28/09/2026) :

      • Python 3.14 — série stable en cours.
      • Python 3.13 — corrections de bogues encore publiées.
      • Python 3.12, 3.11, 3.10 — sécurité seule, plus de fonctionnalités.
      • Python 3.9 — fin de vie, plus aucune mise à jour.

      Premier programme

      def saluer(nom: str) -> str:
          return f"Bonjour, {nom} !"
      
      print(saluer("Alice"))

      Un fichier Python contient des fonctions et des instructions de premier niveau. Le point d'entrée classique est if __name__ == "__main__":, qui permet d'importer le fichier sans exécuter le code.

      La convention de nommage

      • fonctions et variables : snake_case
      • classes : PascalCase
      • constantes : MAJUSCULES

      PEP 8 est la référence de style communautaire. Suivre cette convention, c'est écrire du Python que les autres comprendront sans commentaire.

    3. Installer et exécuter

      Tester sans rien installer

      Pour s'exercer immédiatement, l'interpréteur en ligne officiel permet de exécuter du code dans le navigateur.

      Installer en local

      Sur macOS et Linux, le gestionnaire de paquets du système installe Python. Sur Windows, on installe le paquet officiel. Le principe est le même partout : ensuite, on utilise un environnement virtuel pour isoler les dépendances.

      # créer un environnement, puis l'activer
      python3 -m venv .venv
      source .venv/bin/activate        # macOS / Linux
      .venv\Scripts\activate           # Windows
      
      # installer une bibliothèque dans cet environnement uniquement
      pip install requests

      Pourquoi un environnement virtuel ? Un projet peut demander la version 1 d'une bibliothèque et un autre la version 2. L'environnement virtuel garde les deux séparés. C'est la pratique attendue sur tout projet autre qu'un simple essai.

      Exécuter un programme

      python3 mon_script.py

      Avec les f-strings, l'affichage se fait directement dans la chaîne :

      nom, age = "Alice", 34
      print(f"{nom} aura {age + 1} ans l'an prochain")
  2. Syntaxe, types et structures de contrôle

    1. Variables et types

      Python est à typage dynamique : le type est déduit à la volée, il n'y a pas de déclaration de type. L'annotation (nom: str) est une aide à la lecture et aux outils, pas une contrainte d'exécution.

      entier = 42
      flottant = 3.14
      texte = "Python"
      booleen = True          # True / False, avec une majuscule
      rien = None             # absence de valeur, ce n'est pas 0 ni ""
      
      print(type(entier))     # <class 'int'>
      print(type(rien))       # <class 'NoneType'>

      Les types de base

      • int, float, complex — numériques
      • str — texte, immuable
      • bool — vrai ou faux
      • list, tuple, set, dict — collections
      • NoneType — la valeur None

      Le piège du booléen

      En Python, 0, "", [], {} et None sont faux. Une liste vide est donc fausse, ce qui surprend au premier abord mais évite les tests de longueur everywhere.

      if valeurs:        # faux si la liste est vide
          print("il y a des valeurs")
      
      # explicite et plus lisible quand le type compte
      if len(valeurs) > 0:
          print("il y a des valeurs")

      Chaînes

      nom = "Alice"
      print(nom.upper())      # ALICE  (renvoie une nouvelle chaîne)
      print(nom[0])           # A
      print(nom[-1])          # e
      print(nom[0:3])         # Ali  (découpage)
      print(nom.upper() == "ALICE")  # True
    2. Conditions et boucles

      Conditions

      if note >= 16:
          mention = "Très bien"
      elif note >= 14:
          mention = "Bien"
      elif note >= 10:
          mention = "Passable"
      else:
          mention = "Insuffisant"

      Le test d'appartenance

      Python n'a pas de switch : on utilise match (disponible depuis 3.10) quand on compare une même valeur à plusieurs cas, sinon des if/elif.

      def Notes(note: int) -> str:
          match note:
              case 20:
                  return "Parfait"
              case 19 | 18:
                  return "Excellent"
              case n if n < 10:
                  return "Insuffisant"
              case _:
                  return "Correct"

      Boucles

      for lettre in "Python":
          print(lettre)
      
      for index, valeur in enumerate(["a", "b", "c"]):
          print(index, valeur)
      
      compteur = 0
      while compteur < 3:
          print(compteur)
          compteur += 1

      break sort de la boucle, continue passe à l'itération suivante. else après une boucle s'exécute si aucun break n'a eu lieu — une particularité qui surprend les débutants mais qui exprime bien l'intention « si tout s'est terminé normalement ».

    3. Compréhensions

      La compréhension de liste construit une liste à partir d'un itérable. C'est l'idiomate dominant en Python : plus court, et souvent plus rapide qu'une boucle avec append.

      nombres = [1, 2, 3, 4, 5]
      
      carres = [n * n for n in nombres]              # [1, 4, 9, 16, 25]
      pairs = [n for n in nombres if n % 2 == 0]     # [2, 4]
      
      # avec transformation et condition
      etiquettes = [f"#{n}" for n in nombres if n > 3]   # ['#4', '#5']

      Dictionnaire et ensemble

      longueurs = {mot: len(mot) for mot in ["chat", "chien"]}
      # {'chat': 4, 'chien': 5}
      
      initiales = {mot[0] for mot in ["chat", "chien", "cheval"]}  # {'c'}

      Générateur : la variante paresseuse

      Si vous n'avez besoin que de parcourir la valeur sans la stocker, utilisez une expression génératrice : elle ne construit rien en mémoire.

      carres = (n * n for n in nombres)   # pas de crochets : paresseux
      for c in carres:
          print(c)

      Quand ne pas en utiliser

      Si la logique dépasse une ligne, ou si elle contient une boucle for imbriquée, la compréhension devient illisible : une boucle explicite est alors plus claire.

    4. Exercices

      1. FizzBuzz : afficher les nombres de 1 à 100, en écrivant « Fizz » pour les multiples de 3, « Buzz » pour les multiples de 5, et « FizzBuzz » pour les multiples des deux.
      2. Palindrome : écrire une fonction qui indique si une chaîne est un palindrome, en ignorant la casse et les espaces. Comparer les deux méthodes.
      3. Dictionnaire d'âge : à partir d'une liste de prénoms, construire un dictionnaire prénom → âge, puis afficher les prénoms triés du plus âgé au plus jeune.
      4. Compréhension : réécrivez cette boucle en compréhension.
        mots = ["Python", "est", "simple", "et", "puissant"]
        resultat = []
        for m in mots:
            if len(m) > 3:
                resultat.append(m.upper())
  3. Fonctions, modules et POO

    1. Fonctions

      En Python, une fonction est définie avec def, les paramètres par défaut sont évalués à la définition, et la valeur de retour est return — un return sans valeur arrête la fonction et renvoie None.

      def moyenne(notes: list[float], mention: str = "") -> float:
          """Calcule la moyenne et renvoie un message."""
          if not notes:                       # évite la division par zéro
              raise ValueError("liste de notes vide")
          moyenne = sum(notes) / len(notes)
          if mention:
              return moyenne                  # type ignore
          return moyenne, mention             # un tuple

      Valeurs par défaut mutables : l'erreur classique

      # MAUVAIS : la liste est créée une seule fois, partagée par tous les appels
      def ajouter(elt, cible=[]):
          cible.append(elt)
          return cible
      
      # BON : None par défaut, liste créée à chaque appel
      def ajouter(elt, cible=None):
          if cible is None:
              cible = []
          cible.append(elt)
          return cible

      Arguments nommés et déballage

      def afficher(nom, *, couleur="noir", taille=10):
          print(nom, couleur, taille)
      
      afficher("titre", couleur="rouge")   # lisible, l'ordre n'importe plus
      
      premier, *reste, dernier = [1, 2, 3, 4, 5]
      # premier=1, reste=[2,3,4], dernier=5

      Le * seul rend les paramètres suivants obligatoires et nommés : c'est une façon simple d'éviter les erreurs d'appel.

      Fonctions lambdas et closures

      carre = lambda x: x * x          # une seule expression
      
      def multiplier(facteur):
          def interne(x):
              return x * facteur             # « capture » du facteur
          return interne
      
      double = multiplier(2)
      print(double(21))                     # 42
    2. Classes et objets

      Une classe décrit des données et les méthodes qui les manipulent. Le constructeur est __init__, et la méthode __repr__ définit ce qui s'affiche.

      class CompteBancaire:
          """Compte avec solde contrôlé."""
      
          def __init__(self, titulaire: str, solde: float = 0.0):
              self.titulaire = titulaire
              self._solde = solde            # convention : attribut « privé »
      
          def deposer(self, montant: float) -> bool:
              if montant <= 0:
                  return False
              self._solde += montant
              return True
      
          @property                       # accès en lecture
          def solde(self) -> float:
              return self._solde
      
          def __repr__(self) -> str:
              return f"Compte({self.titulaire}, {self._solde:.2f})"

      @property transforme une méthode en attribut en lecture seule : c.solde au lieu de c.get_solde(), tout en gardant le contrôle. Une classe dataclass évite d'écrire le constructeur à la main :

      from dataclasses import dataclass, field
      
      @dataclass
      class Etudiant:
          nom: str
          age: int
          cours: list[str] = field(default_factory=list)   # pas de partage !
      
      e = Etudiant("Alice", 22, ["Python"])
      print(e)          # Etudiant(nom='Alice', age=22, cours=['Python'])

      Héritage et polymorphisme

      class Forme:
          def aire(self) -> float:
              raise NotImplementedError("à redéfinir")
      
      class Cercle(Forme):
          def __init__(self, rayon: float):
              self.rayon = rayon
      
          def aire(self) -> float:
              return math.pi * self.rayon ** 2
      
      formes = [Cercle(1), Cercle(2)]
      print(sum(f.aire() for f in formes))   # la même méthode, des résultats différents

      La dernière ligne est le polymorphisme : le code ne connaît pas le type réel, il appelle aire().

    3. Modules, imports et décorateurs

      Organiser le code

      mon_projet/
      ├── main.py
      ├── outils/
      │   ├── __init__.py
      │   └── maths.py

      Importer

      from outils.maths import moyenne          # une chose précise
      import outils.maths                           # le module entier
      from outils import maths                      # le sous-module

      On importe un symbole précis plutôt qu'un module entier : on sait d'où vient chaque chose, et on évite les collisions de noms.

      La bibliothèque standard

      Avant d'écrire une fonction, vérifier si elle existe déjà : math, datetime, json, pathlib, csv, random, itertools, collections, unittest.

      import json
      with open("config.json", encoding="utf-8") as f:
          config = json.load(f)          # dict
      
      from pathlib import Path
      for fichier in Path("dossier").glob("*.txt"):
          print(fichier)

      Décorateurs

      Un décorateur est une fonction qui enveloppe une autre fonction. Il sert à la journalisation, aux mesures de temps, aux permissions.

      import functools
      import time
      
      def chronometrer(fonction):
          @functools.wraps(fonction)      # garde le nom et la docstring
          def enveloppe(*args, **kwargs):
              debut = time.perf_counter()
              try:
                  return fonction(*args, **kwargs)
              finally:
                  duree = (time.perf_counter() - debut) * 1000
                  print(f"{fonction.__name__} : {duree:.1f} ms")
          return enveloppe
      
      @chronometrer
      def traitement():
          ...
    4. Exercices

      1. Correction du bug : ce compteur d'appels est-il correct ? Expliquez, puis corrigez.
        def compter_appels(fonction):
            appels = [0]
        
            def enveloppe(*args, **kwargs):
                appels[0] += 1
                return fonction(*args, **kwargs)
            return enveloppe
      2. Catalogue : écrivez une classe Article (nom, prix, quantité) avec une méthode total(), et une fonction qui renvoie le total d'une liste d'articles.
      3. Décorateur : écrivez un décorateur journalise qui affiche les arguments reçus avant l'appel.
  4. Collections, itertools et fichiers

    1. Listes, tuples, ensembles, dictionnaires

      Ces quatre structures couvrent l'essentiel des besoins. Choisir la bonne, c'est déjà une bonne part de la qualité du code.

      # liste : ordonnée, modifiable
      nombres = [3, 1, 2]
      nombres.append(4)          # [3, 1, 2, 4]
      nombres.sort()             # [1, 2, 3, 4]
      nombres.remove(1)
      
      # tuple : ordonné, NON modifiable
      coordonnees = (48.85, 2.35)
      
      # ensemble : sans doublon, non ordonné
      tags = {"python", "web", "python"}    # {'python', 'web'}
      
      # dictionnaire : clé → valeur
      stock = {"clavier": 12}
      stock["souris"] = 40
      stock.get("ecran", 0)                  # 0 au lieu d'une erreur

      Choisir en pratique

      BesoinStructure
      Liste ordonnée, modifiéelist
      Valeurs fixestuple
      Sans doublon, test d'appartenance rapideset
      Accès par clédict

      Déballage et nettoyage

      premier, second = [10, 20]
      cle, valeur = ("a", 1)
      
      notes = [("alice", 15), ("bob", 12), ("alice", 18)]
      mieux = {}
      for nom, note in notes:
          if nom not in mieux or note > mieux[nom]:
              mieux[nom] = note
    2. itertools et functools

      Ces deux modules de la bibliothèque standard remplacent beaucoup de boucles manuelles, en plus lisible et plus rapide.

      import itertools
      
      nombres = [1, 2, 3, 4, 5]
      list(itertools.chain([1, 2], [3, 4]))      # [1, 2, 3, 4]
      list(itertools.combinations(["a", "b", "c"], 2))  # [('a','b'), ('a','c'), ('b','c')]
      list(itertools.groupby(sorted(["a", "ab", "b"])))  # groupes de valeurs voisines
      
      import functools
      f = functools.reduce(lambda acc, x: acc * x, [1, 2, 3, 4])   # 24
      total = functools.partial(sum, [1, 2, 3])      # sum([1,2,3], ...)

      Attention à groupby : il ne groupe que les éléments consécutifs. Il faut trier avant, sinon le résultat est faux.

      enumerate et zip

      for index, valeur in enumerate(["a", "b"], start=1):
          print(index, valeur)          # 1 a  /  2 b
      
      for nom, note in zip(["alice", "bob"], [15, 12]):
          print(nom, note)

      Tri clé

      eleves = [("bob", 12), ("alice", 18), ("carol", 12)]
      # par note décroissante, puis par nom
      eleves.sort(key=lambda e: (-e[1], e[0]))
    3. Fichiers et données

      On lit et on écrit avec open(), toujours dans un bloc with, qui ferme le fichier même en cas d'erreur.

      with open("notes.txt", "w", encoding="utf-8") as f:
          f.write("Alice 15\n")
      
      with open("notes.txt", encoding="utf-8") as f:
          for ligne in f:
              nom, note = ligne.strip().split()
              print(nom, int(note))

      Les modes utiles : r lecture, w écriture (écrase), a ajout, x écriture qui échoue si le fichier existe.

      encoding="utf-8" n'est pas facultatif : sans lui, la lecture dépend de la configuration système et peut échouer sur un accents selon la machine.

      Chemin et dossiers

      from pathlib import Path
      
      dossier = Path("data")
      dossier.mkdir(exist_ok=True)          # ne lève pas si le dossier existe
      chemin = dossier / "notes.txt"
      chemin.write_text("contenu", encoding="utf-8")
      
      for fichier in dossier.glob("*.txt"):
          print(fichier.name, fichier.stat().st_size)

      CSV et JSON

      import csv
      import json
      
      with open("clients.csv", newline="", encoding="utf-8") as f:
          lecteur = csv.DictReader(f)       # première ligne = en-têtes
          for ligne in lecteur:
              print(ligne["nom"], ligne["ville"])
      
      with open("config.json", "w", encoding="utf-8") as f:
          json.dump({"debug": True}, f, indent=2)
      
      with open("config.json", encoding="utf-8") as f:
          config = json.load(f)

      newline="" est requis par csv : sans lui, les retours à la ligne sont mal interprétés sur certains systèmes.

    4. Exercices

      1. Annuaire : à partir d'un fichier CSV contacts.csv (nom, email), compter les doublons d'emails et écrire les doublons dans un autre fichier.
      2. Refactoriser : réécrivez avec itertools.
        paires = []
        for i in range(len(nombres)):
            for j in range(i + 1, len(nombres)):
                paires.append((nombres[i], nombres[j]))
      3. Compte à rebours : écrivez un script qui accepte un nombre en argument de ligne de commande et affiche un compte à rebours, ou une erreur claire si l'argument n'est pas un entier.
  5. Erreurs, exceptions et tests

    1. Exceptions

      Un programme échoue : on ne supprime pas l'erreur, on la traite au bon endroit. try/except sert à anticiper un échec attendu, pas à tout attraper.

      def lire_nombre():
          saisie = input("Un nombre : ")
          try:
              return int(saisie)
          except ValueError:
              print("Ce n'est pas un nombre entier, recommencez.")
              return None

      Capter juste ce qui peut échouer

      # TROP LARGE : masque les vraies erreurs de logique
      try:
          traitement(utilisateur)
      except Exception:
          pass
      
      # JUSTE : on ne traite que l'échec prévu
      try:
          resultat = 10 / diviseur
      except ZeroDivisionError:
          resultat = None

      Lever, propager, documenter

      def retirer(compte, montant):
          if montant <= 0:
              raise ValueError("le montant doit être positif")
          if montant > compte.solde:
              raise ValueError("solde insuffisant")
          compte.deposer(-montant)

      raise signale une situation impossible à contourner. Dans une bibliothèque, on laisse remonter l'exception : l'appelant décidera.

      finally et gestionnaires de contexte

      fichier = None
      try:
          fichier = open("data.txt", encoding="utf-8")
          contenu = fichier.read()
      except FileNotFoundError:
          print("fichier absent")
      else:
          print(f"{len(contenu)} caractères")
      finally:
          if fichier is not None:
              fichier.close()

      En pratique, le with remplace ce try/finally et gère aussi l'echec. Et un objet qui se ferme tout seul peut être écrit avec __enter__ et __exit__.

      Créer sa propre exception

      class SoldeInsuffisant(Exception):
          def __init__(self, manquant: float):
              self.manquant = manquant
              super().__init__(f"il manque {manquant:.2f}")
    2. Tests automatisés

      Un test vérifie une seule chose et échoue clairement. La bibliothèque standard unittest suffit, sans dépendance supplémentaire.

      import unittest
      
      class TestCompteBancaire(unittest.TestCase):
      
          def setUp(self):
              # exécuté avant chaque test : on repart d'un état propre
              self.compte = CompteBancaire("Alice", 100.0)
      
          def test_depot_ajoute_au_solde(self):
              self.compte.deposer(50.0)
              self.assertEqual(self.compte.solde, 150.0)
      
          def test_montant_negatif_refuse(self):
              self.assertFalse(self.compte.deposer(-10.0))
              self.assertEqual(self.compte.solde, 100.0)
      
          def test_solde_vide_leve_une_erreur(self):
              with self.assertRaises(ValueError):
                  moyenne([])

      Les assertions utiles

      • assertEqual, assertTrue, assertIs (identité, pour None)
      • assertIn, assertAlmostEqual pour les flottants
      • assertRaises quand on attend une erreur

      Ce qui rend un test utile

      1. Un nom qui dit ce qui est vérifié (test_montant_negatif_refuse).
      2. Un seul comportement par test : sinon le diagnostic est impossible.
      3. Les cas limites : vide, zéro, négatif, accents, très grand nombre d'éléments.

      Exécuter

      python -m unittest discover -v
      python -m unittest tests.test_calculs
    3. Exercices

      1. Conversion sécurisée : écrivez vers_entier(texte) qui renvoie un entier ou None, et n'utilisez jamais except: seul.
      2. Tester avant de corriger : écrivez trois tests qui échouent sur la version actuelle d'une de vos fonctions, puis corrigez-la. C'est l'ordre professionnel : on ne corrige pas à l'aveugle.
      3. Contexte personnalisé : écrivez une classe Fichier utilisable avec with, qui ouvre le fichier et le ferme automatiquement.
  6. Projet final et méthodologie

    1. Projet fil rouge : gestionnaire de dépenses

      Le projet porte sur tout le cours. Il est volontairement modeste en dépendances — seule la bibliothèque standard — et Livrable avec des tests.

      Le sujet

      Un gestionnaire de dépenses personnelles : ajouter des dépenses, les classer par catégorie, produire un rapport mensuel, et persister les données dans un fichier JSON.

      Contraintes

      1. Un fichier depenses.py pour le modèle et les règles métier, un fichier test_depenses.py pour les tests, un point d'entrée main.py.
      2. Aucune dépendance externe : uniquement la bibliothèque standard.
      3. Chaque fonction doit être courte, testée, et documentée par une docstring.
      4. Les entrées utilisateur invalides produisent un message clair, jamais une trace Python brute.
      5. Les données sont écrites avec json.dump et relues au démarrage.
      6. Au moins 12 tests couvrent le cas nominal, les cas limites et les erreurs.

      Structure attendue

      gestionnaire/
      ├── main.py
      ├── depenses.py
      └── test_depenses.py

      Conduite du projet

      ÉtapeLivrableCritère de fin
      1. Modèle de donnéesclasse Depensetests unitaires au vert
      2. Métierajout, filtrage, rapporttests unitaires au vert
      3. Persistancelecture/écriture JSONaller-retour vérifié
      4. Interfacemenu en consoleparcours complet testé
      5. QualitéPEP 8, docstringsrelecture sans commentaire

      On ne passe à l'étape suivante que quand la précédente est vérifiée. C'est ce qui distingue un projet qui avance d'un projet qui s'épuise.

    2. Méthodologie et bonnes pratiques

      Les règles qui font la différence

      1. Lisibilité avant astuce. Si une expression demande une seconde de réflexion, elle est trop compliquée.
      2. Le code lu plus souvent qu'il n'est écrit. Nommer clairement vaut mieux que commenter.
      3. Une fonction, une responsabilité. Une fonction qui fait trois choses ne se teste pas.
      4. La bibliothèque standard d'abord. Écrire soi-même ce qu'elle fournit est une perte de temps.
      5. Gérer l'erreur là où elle est prévisible. Pas de except silencieux.

      PEP 8, l'essentiel

      • quatre espaces d'indentation, jamais des tabulations
      • 79 à 99 caractères par ligne
      • deux lignes vides entre les fonctions de haut niveau
      • snake_case pour les fonctions et variables, PascalCase pour les classes

      Outils utiles

      • python -m venv et pip pour l'environnement et les dépendances
      • unittest pour les tests (inclus)
      • pip install ruff : vérificateur de style et de bogues, très rapide
      • pip install black : formatage automatique

      Un projet sans requirements.txt n'est pas reproductible : la liste des dépendances fait partie du code.

      Pour aller plus loin

      1. Données : pandas pour l'analyse, matplotlib pour les graphiques.
      2. Web : Flask (léger) ou Django (complet).
      3. Automatisation : openpyxl pour Excel, requests pour les API.
      4. Tests de style : pytest, alternative moderne à unittest.
    3. Ressources et évaluation

      Méthodes d'évaluation

      • Quiz à la fin de chaque module, sur la syntaxe et les concepts.
      • Atelier : un exercice guidé avec livrable.
      • Projet final : évalué sur les tests, la lisibilité et le respect des contraintes.
      • Code review : chaque atelier est relu avec des remarques concrètes.

      Critères d'évaluation du projet

      • fonctionnel : toutes les contraintes sont respectées
      • qualité : tests au vert, code relu, style respecté
      • progressé : étapes livrées dans l'ordre, avec commits identifiables
Sources et documentation de reference

Atelier guidé

Reprendre un script existant et le rendre lisible par quelqu'un d'autre : nommer, decomposer, tester.

  1. Structure : decouper un script de plus de 150 lignes en fonctions qui font chacune une chose, et supprimer la duplication.
  2. Donnees : remplacer les listes paralleles par des dictionnaires, puis valider les entrees avant usage.
  3. Tests : ecrire un test par fonction, dont un cas limite : liste vide, chaine absente, division par zero.
  4. Lecture : relire le code sans la machine et nommer ce qui ne va pas — puis le corriger.

Restitution de l’atelier

  • Le script decompose, avec le nom de chaque fonction.
  • Une suite de tests qui passe, et le rapport de couverture.
  • Une note de deux paragraphes sur les trois difficultes rencontrees.
Validation : Un tiers peut modifier une fonction sans lire les autres. Chaque fonction a un test qui echoue si on la casse.

Publié le 28/09/2026