Club D.I.A.M · Corrections Officielles

Corrections PythonBootcamp 10 Jours — Solutions Complètes & Expliquées

57 exercices corrigés avec explications. Chaque solution est testée et commentée.

1

Fondations Python

Variables · Types · Arithmétique · Input/Output · Strings

Variables et types int, float, str, bool
Opérations arithmétiques
Input / print / f-strings
Méthodes strings et slicing
Ex 1.1 Hello World Personnalisé

Un profil développeur formaté avec des séparateurs visuels.

💡
La multiplication d'une chaîne "=" * 50 répète le caractère 50 fois.
ex_1_1.py
print("=" * 50)
print("        MON PROFIL DÉVELOPPEUR DIAM")
print("=" * 50)
print("  Nom       : Jean Dupont")
print("  Ville     : Libreville")
print("  Objectif  : Python & Data Science")
print("  Club      : D.I.A.M")
print("=" * 50)
Ex 1.2 Déclaration de Variables

Six variables de types différents affichées avec des f-strings.

ex_1_2.py
prenom       = "Jean"
nom          = "Dupont"
age          = 22
ville        = "Libreville"
taille       = 1.78
est_etudiant = True

print(f"Prénom   : {prenom}")
print(f"Nom      : {nom}")
print(f"Âge      : {age} ans")
print(f"Ville    : {ville}")
print(f"Taille   : {taille} m")
print(f"Étudiant : {est_etudiant}")
Ex 1.3 Vérification des Types

La fonction type() retourne le type Python de chaque valeur.

💡
10 est un int, on peut calculer avec. "123" est une str, impossible d'additionner avec un entier.
ex_1_3.py
valeurs = [10, 3.14, "Python", True, "123"]
for v in valeurs:
    print(f"  {str(v):<10} -> type : {type(v).__name__}")

# DIFFÉRENCE : 10 (int) et "123" (str)
# 10 + 5 = 15   ->  calcul arithmétique possible
# "123" + 5     ->  TypeError : impossible !
# "123" + "5"   = "1235"  (concaténation)
Ex 1.4 Calculatrice de Base

Les 7 opérateurs arithmétiques Python sur deux valeurs.

ex_1_4.py
a, b = 17, 5
print(f"{a} +  {b} = {a + b}")     # 22
print(f"{a} -  {b} = {a - b}")     # 12
print(f"{a} *  {b} = {a * b}")     # 85
print(f"{a} /  {b} = {a / b}")     # 3.4
print(f"{a} // {b} = {a // b}")    # 3  (div entière)
print(f"{a} %  {b} = {a % b}")     # 2  (reste)
print(f"{a} ** {b} = {a ** b}")    # 1 419 857 (puissance)
Ex 1.5 Calculs Pratiques — Géométrie & Conversion

Trois calculs mathématiques classiques en Python.

ex_1_5.py
# Aire d'un rectangle
longueur, largeur = 12, 5
aire_rect = longueur * largeur
print(f"Aire rectangle  : {aire_rect} m²")

# Aire d'un cercle  r = 7
PI = 3.14159
r  = 7
aire_cercle = PI * r ** 2
print(f"Aire cercle     : {aire_cercle:.4f} m²")

# Conversion Celsius -> Fahrenheit
celsius    = 25
fahrenheit = celsius * 9 / 5 + 32
print(f"{celsius}°C = {fahrenheit}°F")
Ex 1.6 Calculatrice Interactive

input() retourne une chaîne → conversion en float() obligatoire.

ex_1_6.py
a = float(input("Premier nombre  : "))
b = float(input("Deuxième nombre : "))

print(f"Somme      : {a} + {b} = {a + b}")
print(f"Différence : {a} - {b} = {a - b}")
print(f"class>="cls">Produit    : {a} * {b} = {a * b}")
if b != 0:
    print(f"Quotient   : {a} / {b} = {a / b:.4f}")
else:
    print("Quotient   : Division par zéro impossible !")
Ex 1.7 Calculateur d'Année de Naissance

Soustraction simple à partir de l'année actuelle.

ex_1_7.py
age              = int(input("Votre âge : "))
annee_naissance  = 2025 - age
print(f"Vous êtes né(e) vers {annee_naissance}.")
Ex 1.8 Convertisseur de Distances

Multiplication par le facteur de conversion, affichage à 2 décimales.

ex_1_8.py
km    = float(input("Distance en kilomètres : "))
miles = km * 0.621371
print(f"{km} km = {miles:.2f} miles")
Ex 1.9 Manipulation de Texte

Enchaînement des méthodes string sur la chaîne de départ.

ex_1_9.py
texte = "   PYTHON EST PUISSANT   "

print(f"Original     : &#x27;{texte}'")
print(f"strip()      : &#x27;{texte.strip()}'")
print(f"lower()      : &#x27;{texte.strip().lower()}'")
print(f"capitalize() : &#x27;{texte.strip().lower().capitalize()}'")
print(f"Nb de &#x27;P'    : {texte.count('P')}")
print(f"replace()    : &#x27;{texte.strip().replace('PUISSANT', 'GÉNIAL')}'")
Ex 1.10 Formateur de Nom + Email Généré

Combinaison de title(), upper(), lower() et indexation de chaîne.

ex_1_10.py
prenom    = input("Votre prénom : ").strip().title()
nom       = input("Votre nom   : ").strip().upper()

initiales = f"{prenom[0]}.{nom[0]}."
email     = f"{prenom.lower()}.{nom.lower()}class>="fn">@diam.ga"

print(f"Prénom    : {prenom}")
print(f"Nom       : {nom}")
print(f"Initiales : {initiales}")
print(f"Email     : {email}")
Ex 1.12 Vérificateur de Palindrome

Le slicing [::-1] inverse une chaîne — on compare avec l'original.

💡
[::-1] = slicing avec pas -1 → parcours de droite à gauche.
ex_1_12.py
mot         = input("Entrez un mot : ").lower().strip()
mot_inverse = mot[::-1]

print(f"Mot     : {mot}")
print(f"Inversé : {mot_inverse}")

if mot == mot_inverse:
    print(f"✅ &#x27;{mot}' EST un palindrome !")
else:
    print(f"❌ &#x27;{mot}' n'est PAS un palindrome.")
Soir 1.13 Soir 1.13 — Convertisseur Multi-Unités

Trois conversions en une seule session interactive.

soir_1_13.py
km      = float(input("Distance en km     : "))
celsius = float(input("Température en °C  : "))
kg      = float(input("Poids en kg        : "))

miles      = km * 0.621371
fahrenheit = celsius * 9 / 5 + 32
livres     = kg * 2.205

print(f"\n{km} km   -> {miles:.3f} miles")
print(f"{celsius}°C  -> {fahrenheit:.1f}°F")
print(f"{kg} kg  -> {livres:.3f} livres")
Soir 1.14 Soir 1.14 — Calculateur de Facture

Calcul HT → TVA 18% → TTC avec affichage formaté.

soir_1_14.py
prix_ht  = float(input("Prix unitaire HT (FCFA) : "))
quantite = int(input("Quantité                : "))
TVA      = 0.18

sous_total_ht = prix_ht * quantite
montant_tva   = sous_total_ht * TVA
total_ttc     = sous_total_ht + montant_tva

print("\n" + "=" * 44)
print("          FACTURE D.I.A.M")
print("=" * 44)
print(f"  {&#x27;Prix unitaire HT':<22} : {prix_ht:>9,.0f} F")
print(f"  {&#x27;Quantité':<22} : {quantite:>9}")
print(f"  {&#x27;Sous-total HT':<22} : {sous_total_ht:>9,.0f} F")
print(f"  {&#x27;TVA (18%)':<22} : {montant_tva:>9,.0f} F")
print("-" * 44)
print(f"  {&#x27;TOTAL TTC':<22} : {total_ttc:>9,.0f} F")
print("=" * 44)
Soir 1.15 Soir 1.15 — Carte de Visite ASCII

Calcul dynamique de la largeur pour encadrer le texte le plus long.

soir_1_15.py
nom        = input("Nom complet  : ").strip()
profession = input("Profession   : ").strip()
telephone  = input("Téléphone    : ").strip()
email      = input("Email        : ").strip()

w = max(len(nom), len(profession), len(telephone), len(email)) + 6

print("╔" + "═" * w + "╗")
print("║" + " " * w + "║")
print(f"║  {nom:<{w-2}}║")
print(f"║  {profession:<{w-2}}║")
print("║" + " " * w + "║")
print(f"║  📞 {telephone:<{w-5}}║")
print(f"║  ✉  {email:<{w-5}}║")
print("║" + " " * w + "║")
print("╚" + "═" * w + "╝")
2

Structures de Contrôle

if/elif/else · Opérateurs logiques · while · for · break/continue

Conditions if/elif/else
Opérateurs de comparaison et logiques
Boucles while avec break/continue
Boucles for et range()
Ex 2.1 Vérificateur de Majorité

Trois cas : âge invalide / mineur (avec années restantes) / majeur.

💡
isdigit() vérifie que la saisie est un entier positif avant la conversion.
ex_2_1.py
saisie = input("Entrez votre âge : ")
if not saisie.isdigit():
    print("❌ Âge invalide (entrez un entier positif).")
else:
    age = int(saisie)
    if age > 150:
        print("❌ Âge invalide (trop grand).")
    elif age < 18:
        print(f"⏳ Vous êtes mineur. Encore {18 - age} an(s) avant la majorité.")
    else:
        print(f"✅ Vous êtes majeur ({age} ans).")
Ex 2.2 Calculateur de Mention

Cinq paliers avec validation de la plage 0-20.

ex_2_2.py
try:
    note = float(input("Note (/20) : "))
    if not (0 <= note <= 20):
        print("❌ Note invalide (entre 0 et 20).")
    elif note >= 16: print(f"{note}/20 -> Mention Très Bien 🏆")
    elif note >= 14: print(f"{note}/20 -> Mention Bien 🥇")
    elif note >= 12: print(f"{note}/20 -> Mention Assez Bien 🥈")
    elif note >= 10: print(f"{note}/20 -> Mention Passable ✅")
    else:            print(f"{note}/20 -> Insuffisant ❌")
except class>="cls">ValueError:
    print("❌ Entrez un nombre valide.")
Ex 2.4 Calcul de Réduction

Les seuils doivent être testés du plus grand au plus petit.

ex_2_4.py
montant = float(input("Montant d&#x27;achat (FCFA) : "))

if   montant >= 100_000: taux = 0.20
elif montant >=  50_000: taux = 0.15
elif montant >=  20_000: taux = 0.10
else:                    taux = 0.0

reduction = montant * taux
total     = montant - reduction

print(f"  Montant initial  : {montant:>12,.0f} FCFA")
print(f"  Réduction ({taux*100:.0f}%)   : {reduction:>12,.0f} FCFA")
print(f"  Total à payer    : {total:>12,.0f} FCFA")
Ex 2.5 Année Bissextile

Règle exacte : divisible par 4 MAIS pas par 100, SAUF par 400.

💡
L'ordre des conditions est important : tester % 400 avant (ou via or en dehors).
ex_2_5.py
annee = int(input("Entrez une année : "))

if (annee % 4 == 0 and annee % 100 != 0) or (annee % 400 == 0):
    print(f"✅ {annee} est une année bissextile.")
else:
    print(f"❌ {annee} n&#x27;est pas une année bissextile.")

# Tests : 2000 ✅  |  1900 ❌  |  2024 ✅  |  2025 ❌
Ex 2.7 Pierre-Feuille-Ciseaux

random.choice() pour l'ordinateur, if/elif/else pour déterminer le gagnant.

ex_2_7.py
import random

choix_valides = ["pierre", "feuille", "ciseaux"]
choix_ordi    = random.choice(choix_valides)
choix_joueur  = input("Pierre, Feuille ou Ciseaux ? ").strip().lower()

if choix_joueur not in choix_valides:
    print("❌ Choix invalide !")
else:
    print(f"  Vous       : {choix_joueur}")
    print(f"  Ordinateur : {choix_ordi}")
    if choix_joueur == choix_ordi:
        print("  🤝 Égalité !")
    elif ((choix_joueur == "pierre"  and choix_ordi == "ciseaux") or
          (choix_joueur == "feuille" and choix_ordi == "pierre")  or
          (choix_joueur == "ciseaux" and choix_ordi == "feuille")):
        print("  🏆 Vous gagnez !")
    else:
        print("  💻 L&#x27;ordinateur gagne !")
Ex 2.9 Validation d'Âge (while)

Boucle infinie + isdigit() + vérification de plage.

💡
isdigit() retourne True si tous les caractères sont des chiffres.
ex_2_9.py
while True:
    saisie = input("Entrez votre âge (0-120) : ")
    if not saisie.isdigit():
        print("  ❌ Entrez un nombre entier positif.")
        continue
    age = int(saisie)
    if 0 <= age <= 120:
        print(f"  ✅ Âge valide : {age} ans")
        break
    else:
        print("  ❌ L&#x27;âge doit être entre 0 et 120.")
Ex 2.10 Jeu de Devinette

randint(1, 100) + indices Plus grand / Plus petit + compteur de tentatives.

💡
Le bloc else du while s'exécute quand la boucle se termine sans break.
ex_2_10.py
import random

secret         = random.randint(1, 100)
tentatives     = 0
MAX_TENTATIVES = 10

print("🎯 Devinez le nombre entre 1 et 100 !")

while tentatives < MAX_TENTATIVES:
    try:
        guess = int(input(f"Tentative {tentatives+1}/{MAX_TENTATIVES} : "))
        tentatives += 1
        if guess == secret:
            print(f"🏆 Bravo ! Trouvé en {tentatives} tentative(s) !")
            break
        elif guess < secret:
            print("  📈 Plus grand !")
        else:
            print("  📉 Plus petit !")
    except class>="cls">ValueError:
        print("  ❌ Entrez un nombre entier.")
else:
    print(f"😞 Perdu ! Le nombre était {secret}.")
Ex 2.12 Menu Interactif

Boucle infinie avec break sur l'option Quitter.

ex_2_12.py
while True:
    print("\n" + "─" * 28)
    print("     MENU PRINCIPAL")
    print("─" * 28)
    print("  1. Afficher un message")
    print("  2. Calculer le carré")
    print("  3. Compter jusqu&#x27;à 10")
    print("  4. Quitter")
    print("─" * 28)
    choix = input("Choix (1-4) : ").strip()

    if choix == "1":
        print(f"  📢 {input(&#x27;Message : ')}")
    elif choix == "2":
        try:
            n = float(input("Nombre : "))
            print(f"  {n}² = {n**2}")
        except class>="cls">ValueError:
            print("  ❌ Nombre invalide.")
    elif choix == "3":
        print("  ", end="")
        for i in range(1, 11):
            print(i, end=" ")
        print()
    elif choix == "4":
        print("  👋 Au revoir !")
        break
    else:
        print("  ❌ Choix invalide.")
Ex 2.13 Table de Multiplication

range(1, 11) pour itérer de 1 à 10, affichage formaté avec :.

ex_2_13.py
n = int(input("Entrez un nombre : "))
print(f"\n{&#x27;─'*24}")
print(f"     TABLE DE {n}")
print(f"{&#x27;─'*24}")
for i in range(1, 11):
    print(f"  {n:3d} × {i:2d} = {n * i:5d}")
print(f"{&#x27;─'*24}")
Ex 2.15 Factorielle

Accumulateur initialisé à 1 multiplié à chaque tour de boucle.

💡
Note : range(1, n+1) va bien jusqu'à n inclus.
ex_2_15.py
n = int(input("Entrez N : "))
if n < 0:
    print("❌ La factorielle n&#x27;est pas définie pour les négatifs.")
else:
    factorielle = 1
    for i in range(1, n + 1):
        factorielle *= i
    print(f"{n}! = {factorielle}")

# Vérifications : 0!=1  |  5!=120  |  10!=3628800
Ex 2.19 Sapin de Noël

Formule : espaces = h - i et étoiles = 2*i - 1.

💡
À chaque ligne i : h-i espaces devant, 2i-1 étoiles.
ex_2_19.py
h = int(input("Hauteur : "))
for i in range(1, h + 1):
    print(" " * (h - i) + "*" * (2 * i - 1))

# Pour h=5 :
#     *
#    ***
#   *****
#  *******
# *********
Ex 2.20 FizzBuzz Classique

Tester d'abord % 15 (divisible par 3 ET 5) avant les cas simples.

💡
Si on testait % 3 en premier, les multiples de 15 afficheraient juste "Fizz" et jamais "FizzBuzz".
ex_2_20.py
for i in range(1, 101):
    if   i % 15 == 0: print("FizzBuzz")
    elif i % 3  == 0: print("Fizz")
    elif i % 5  == 0: print("Buzz")
    else:              print(i)
3

Fonctions

def · Paramètres · return · Scope · Lambda · *args/**kwargs

Définir et appeler des fonctions
Paramètres, valeurs par défaut
*args pour nombre variable d'arguments
Fonctions lambda
Ex 3.3 Calculatrice Modulaire

4 fonctions distinctes, la division gère le cas division par zéro.

ex_3_3.py
def additionner(a, b): return a + b
def soustraire(a, b):  return a - b
def multiplier(a, b):  return a * b

def diviser(a, b):
    if b == 0:
        return "Erreur : division par zéro !"
    return a / b

a = float(input("Premier nombre  : "))
b = float(input("Deuxième nombre : "))
print(f"{a} + {b} = {additionner(a, b)}")
print(f"{a} - {b} = {soustraire(a, b)}")
print(f"{a} * {b} = {multiplier(a, b)}")
print(f"{a} / {b} = {diviser(a, b)}")
Ex 3.6 Calculateur de Moyenne (*args)

*notes capte n'importe combien d'arguments dans un tuple.

💡
if not notes est vrai quand le tuple est vide.
ex_3_6.py
def calculer_moyenne(*notes):
    """Retourne la moyenne d&#x27;un nombre quelconque de notes."""
    if not notes:
        return "Aucune note fournie."
    return sum(notes) / len(notes)

print(calculer_moyenne(15, 18, 12, 16))   # 15.25
print(calculer_moyenne(10))               # 10.0
print(calculer_moyenne())                 # message d'erreur
Ex 3.12 Validateur de Mot de Passe

Quatre règles vérifiées avec any() et des expressions génératrices.

💡
any(c.isupper() for c in mdp) retourne True dès qu'une majuscule est trouvée.
ex_3_12.py
def valider_mot_de_passe(mdp):
    """Vérifie : >=8 chars, >=1 maj, >=1 min, >=1 chiffre."""
    if len(mdp) < 8:
        return False, "❌ Trop court (minimum 8 caractères)"
    if not any(c.isupper() for c in mdp):
        return False, "❌ Aucune lettre majuscule"
    if not any(c.islower() for c in mdp):
        return False, "❌ Aucune lettre minuscule"
    if not any(c.isdigit() for c in mdp):
        return False, "❌ Aucun chiffre"
    return True, "✅ Mot de passe valide !"

for test in ["abc", "password", "Password", "Password1"]:
    ok, msg = valider_mot_de_passe(test)
    print(f"  &#x27;{test:<12}' -> {msg}")
Ex 3.13 Générateur de Fibonacci

Initialisation avec [0, 1] puis ajout de la somme des deux derniers.

💡
suite[-1] = dernier élément, suite[-2] = avant-dernier.
ex_3_13.py
def fibonacci(n):
    """Retourne les n premiers termes de Fibonacci."""
    if n <= 0: return []
    if n == 1: return [0]
    suite = [0, 1]
    for _ in range(2, n):
        suite.append(suite[-1] + suite[-2])
    return suite

print(fibonacci(10))  # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
print(fibonacci(1))   # [0]
print(fibonacci(0))   # []
4

Listes & Tuples

Listes · Méthodes · Slicing · List Comprehensions · Tuples

Créer et manipuler des listes
Indexation et slicing
List comprehensions
Tuples et unpacking
Ex 4.1 Gestion de Courses

insert() ajoute à un index précis, del supprime par index.

ex_4_1.py
courses = ["Riz", "Sucre", "Huile", "Farine", "Sel"]
print("Initial     :", courses)

courses.insert(1, "Lait")       # Insère en position 1
print("+ Lait idx1 :", courses)

del courses[2]                  # Supprime le 3ème élément
print("- idx2      :", courses)

print("&#x27;Eau' présent ?", "Eau" in courses)  # False
Ex 4.2 Manipulation de Nombres

Tri, statistiques avec les fonctions intégrées Python.

ex_4_2.py
nombres = [12, 5, 23, 7, 15, 8, 30, 4]
nombres.sort()
print(f"Triée   : {nombres}")
print(f"Max     : {max(nombres)}")
print(f"Min     : {min(nombres)}")
print(f"Somme   : {sum(nombres)}")
print(f"Moyenne : {sum(nombres)/len(nombres):.2f}")
Ex 4.7 List Comprehensions Practice

4 comprehensions : carrés, multiples, conversion, booléens.

ex_4_7.py
# Carrés de 0 à 9
carres = [x**2 for x in range(10)]
print("Carrés      :", carres)

# Multiples de 3 jusqu'à 30
mult3 = [x for x in range(0, 31, 3)]
print("Multiples 3 :", mult3)

# Conversion ["1","2","3"] -> [1, 2, 3]
int_nums = [int(x) for x in ["1", "2", "3"]]
print("Conversion  :", int_nums)

# Booléens (> 5)
valeurs  = [3, 7, 1, 9, 4, 6]
bool_lst = [x > 5 for x in valeurs]
print("Booléens >5 :", bool_lst)
Ex 4.9 Retours Multiples avec Tuple

Une fonction retourne un tuple que l'on déstructure à l'appel.

ex_4_9.py
def calculer_stats_liste(nombres):
    """Retourne (min, max, somme, moyenne) sous forme de tuple."""
    if not nombres:
        return None
    return (min(nombres), max(nombres), sum(nombres), sum(nombres) / len(nombres))

stats = calculer_stats_liste([12, 5, 23, 7, 15, 8, 30, 4])
mini, maxi, somme, moyenne = stats  # unpacking du tuple
print(f"Min={mini}, Max={maxi}, Somme={somme}, Moy={moyenne:.2f}")
5

Dictionnaires & Sets

Paires clé-valeur · Méthodes · Dict Comprehensions · Ensembles

Créer et parcourir des dictionnaires
Dict comprehensions
Opérations ensemblistes (|, &, -)
Dédoublonnage
Ex 5.3 Compteur de Mots

dict.get(mot, 0) + 1 : valeur par défaut 0 si le mot est absent.

ex_5_3.py
phrase   = "le chien mange le repas et le chat mange aussi"
mots     = phrase.lower().split()
compteur = {}

for mot in mots:
    compteur[mot] = compteur.get(mot, 0) + 1

# Tri par fréquence décroissante
for mot, nb in sorted(compteur.items(), key=lambda x: x[1], reverse=True):
    print(f"  {mot:<12} : {nb}")
Ex 5.8 Opérations Ensemblistes

Les 4 opérations sur les ensembles Python : union, intersection, différence, symétrique.

ex_5_8.py
groupe_a = {"Alice", "Bob", "Charlie", "Diana", "Eve"}
groupe_b = {"Charlie", "Diana", "Frank", "Grace", "Heidi"}

print("Union          :", groupe_a | groupe_b)
print("Intersection   :", groupe_a & groupe_b)
print("Uniquement A   :", groupe_a - groupe_b)
print("Uniquement B   :", groupe_b - groupe_a)
print("Dans un seul   :", groupe_a ^ groupe_b)
Ex 5.9 Dédoublonnage avec Ordre Préservé

Un set() suit les éléments vus ; list conserve l'ordre.

💡
Simple list(set(liste)) supprimerait bien les doublons mais perdrait l'ordre.
ex_5_9.py
def dedoublonner(liste):
    """Enlève les doublons EN PRÉSERVANT l&#x27;ordre d'apparition."""
    vus      = set()
    resultat = []
    for element in liste:
        if element not in vus:
            vus.add(element)
            resultat.append(element)
    return resultat

test = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
print("Original    :", test)
print("Dédoublonné :", dedoublonner(test))
# [3, 1, 4, 5, 9, 2, 6]  — ordre préservé !
6

Fichiers & Exceptions

Lecture/Écriture · CSV · JSON · try/except · pathlib

Lire / écrire des fichiers texte
Modules csv et json
try/except/else/finally
Gestion des erreurs courantes
Ex 6.1 Créer un Journal (Diary)

Mode "a" pour ajouter sans écraser, "r" pour lire.

💡
Toujours utiliser with open(...) — le fichier est fermé automatiquement.
ex_6_1.py
from class>="cls">datetime import class>="cls">datetime

def ajouter_entree(fichier="journal.txt"):
    entree = input("Note du jour : ")
    date   = class>="cls">datetime.now().strftime("%d/%m/%Y %H:%M")
    with open(fichier, "a", encoding="utf-8") as f:
        f.write(f"[{date}] {entree}\n")
    print("✅ Entrée ajoutée !")

def lire_journal(fichier="journal.txt"):
    try:
        with open(fichier, "r", encoding="utf-8") as f:
            contenu = f.read()
        print("─── JOURNAL ───")
        print(contenu if contenu else "📔 Journal vide.")
    except class>="cls">FileNotFoundError:
        print("📔 Aucun journal trouvé.")

ajouter_entree()
lire_journal()
Ex 6.2 Compteur de Mots dans un Fichier

Lecture complète puis comptage avec splitlines() et split().

ex_6_2.py
def analyser_fichier(chemin):
    """Retourne {lignes, mots, caracteres}."""
    try:
        with open(chemin, "r", encoding="utf-8") as f:
            contenu = f.read()
        return {
            "lignes"    : len(contenu.splitlines()),
            "mots"      : len(contenu.split()),
            "caracteres": len(contenu)
        }
    except class>="cls">FileNotFoundError:
        print(f"❌ Fichier &#x27;{chemin}' introuvable.")
        return None

stats = analyser_fichier("mon_texte.txt")
if stats:
    print(f"Lignes     : {stats[&#x27;lignes']}")
    print(f"Mots       : {stats[&#x27;mots']}")
    print(f"Caractères : {stats[&#x27;caracteres']}")
Ex 6.4 Gestionnaire de Notes CSV

DictWriter/DictReader pour lire et écrire des CSV structurés.

ex_6_4.py
import csv
from pathlib import class>="cls">Path

FICHIER = "notes.csv"

def ajouter_note_csv(nom, matiere, note):
    existe = class>="cls">Path(FICHIER).exists()
    with open(FICHIER, "a", newline="", encoding="utf-8") as f:
        w = csv.writer(f)
        if not existe:
            w.writerow(["Nom", "Matiere", "Note"])
        w.writerow([nom, matiere, note])

def lire_notes_csv():
    try:
        with open(FICHIER, "r", encoding="utf-8") as f:
            for ligne in csv.DictReader(f):
                print(f"  {ligne[&#x27;Nom']:<15} {ligne['Matiere']:<15} {ligne['Note']}")
    except class>="cls">FileNotFoundError:
        print("❌ Aucun fichier de notes.")

def calculer_moyenne_etudiant(nom):
    notes = []
    with open(FICHIER, "r", encoding="utf-8") as f:
        for l in csv.DictReader(f):
            if l["Nom"] == nom:
                notes.append(float(l["Note"]))
    return sum(notes) / len(notes) if notes else None

ajouter_note_csv("Alice", "Maths", 16)
ajouter_note_csv("Alice", "Physique", 14)
lire_notes_csv()
print(f"Moyenne Alice : {calculer_moyenne_etudiant(&#x27;Alice'):.2f}")
Ex 6.5 Base de Données JSON Persistante

json.load() au chargement, json.dump() à la sauvegarde.

💡
ensure_ascii=False permet d'enregistrer les caractères accentués correctement.
ex_6_5.py
import json
from pathlib import class>="cls">Path

FICHIER = "contacts.json"

def charger_base():
    if class>="cls">Path(FICHIER).exists():
        with open(FICHIER, "r", encoding="utf-8") as f:
            return json.load(f)
    return {}

def sauvegarder_base(base):
    with open(FICHIER, "w", encoding="utf-8") as f:
        json.dump(base, f, indent=4, ensure_ascii=False)

def ajouter_personne(nom, age, ville):
    base = charger_base()
    base[nom] = {"age": age, "ville": ville}
    sauvegarder_base(base)
    print(f"✅ {nom} ajouté(e).")

def rechercher_personne(nom):
    base = charger_base()
    if nom in base:
        p = base[nom]
        print(f"✅ {nom} : {p[&#x27;age']} ans, {p['ville']}")
    else:
        print(f"❌ &#x27;{nom}' non trouvé(e).")

ajouter_personne("Alice", 25, "Libreville")
rechercher_personne("Alice")
rechercher_personne("Charlie")
Ex 6.6 Calculatrice Sécurisée

try/except/else/finally : chaque bloc a un rôle précis.

💡
else s'exécute uniquement si aucune exception n'a été levée. finally s'exécute dans tous les cas.
ex_6_6.py
def calculatrice_securisee():
    try:
        a  = float(input("Premier nombre  : "))
        op = input("Opération (+,-,*,/) : ").strip()
        b  = float(input("Deuxième nombre : "))

        if   op == "+": resultat = a + b
        elif op == "-": resultat = a - b
        elif op == "*": resultat = a * b
        elif op == "/": resultat = a / b
        else: raise class>="cls">ValueError(f"Opération &#x27;{op}' inconnue.")

    except class>="cls">ValueError as e:
        print(f"❌ Saisie invalide : {e}")
    except class>="cls">ZeroDivisionError:
        print("❌ Division par zéro impossible !")
    else:
        print(f"✅ {a} {op} {b} = {resultat}")  # si aucune erreur
    finally:
        print("🔄 Calcul terminé.")              # toujours

calculatrice_securisee()
Ex 6.7 Lecture Sécurisée de Fichier

Trois types d'erreurs différents → trois messages différents.

ex_6_7.py
def lire_fichier_securise(chemin):
    try:
        with open(chemin, "r", encoding="utf-8") as f:
            contenu = f.read()
        if not contenu:
            print("⚠️  Le fichier est vide.")
            return None
        return contenu
    except class>="cls">FileNotFoundError:
        print(f"❌ Fichier &#x27;{chemin}' introuvable.")
    except class>="cls">PermissionError:
        print(f"❌ Permission refusée pour &#x27;{chemin}'.")
    except class>="cls">UnicodeDecodeError:
        print(f"❌ Erreur d&#x27;encodage — essayez encoding='latin-1'.")
    return None

contenu = lire_fichier_securise("mon_fichier.txt")
if contenu:
    print(contenu[:200])
Soir 6.8 Soir 6.8 — Analyseur de Log

Filtrage par mots-clés avec in, dédoublonnage avec set().

soir_6_8.py
def analyser_log(chemin):
    try:
        with open(chemin, "r", encoding="utf-8") as f:
            lignes = f.readlines()
    except class>="cls">FileNotFoundError:
        print("❌ Fichier log introuvable.")
        return

    erreurs   = [l for l in lignes if "ERROR"   in l]
    warnings  = [l for l in lignes if "WARNING" in l]
    msgs_uniq = set(l.strip() for l in erreurs)

    print(f"  Total lignes   : {len(lignes)}")
    print(f"  Erreurs        : {len(erreurs)}")
    print(f"  Avertissements : {len(warnings)}")
    print(f"  Erreurs uniques: {len(msgs_uniq)}")
    for m in msgs_uniq:
        print(f"    -> {m}")

analyser_log("app.log")
Soir 6.9 Soir 6.9 — Fusion de Fichiers

Boucle sur la liste de fichiers, gestion individuelle de chaque erreur.

soir_6_9.py
def fusionner_fichiers(fichiers, sortie):
    """Fusionne plusieurs fichiers texte en un seul."""
    with open(sortie, "w", encoding="utf-8") as f_out:
        for chemin in fichiers:
            try:
                with open(chemin, "r", encoding="utf-8") as f_in:
                    f_out.write(f"=== {chemin} ===\n")
                    f_out.write(f_in.read())
                    f_out.write("\n\n")
                print(f"  ✅ &#x27;{chemin}' fusionné.")
            except class>="cls">FileNotFoundError:
                print(f"  ⚠️  &#x27;{chemin}' introuvable — ignoré.")
            except class>="cls">PermissionError:
                print(f"  ⚠️  Permission refusée pour &#x27;{chemin}' — ignoré.")
    print(f"  ✅ Fusion terminée -> &#x27;{sortie}'")

fusionner_fichiers(["fichier1.txt", "fichier2.txt"], "fusion.txt")
7

Programmation Orientée Objet

Classes · __init__ · Héritage · Encapsulation · Polymorphisme

Créer des classes et des objets
Encapsulation avec @property
Héritage avec super()
Polymorphisme et méthodes spéciales
Ex 7.1 Classe Voiture

__init__ initialise les attributs, les méthodes agissent sur self.

💡
__str__ est appelé automatiquement par print().
ex_7_1.py
class class>="cls">Voiture:
    def __init__(self, marque, modele, annee):
        self.marque = marque
        self.modele = modele
        self.annee  = annee

    def afficher_info(self):
        print(f"  🚗 {self.marque} {self.modele} ({self.annee})")

    def calculer_age(self):
        return 2025 - self.annee

    def __str__(self):
        return f"class>="cls">Voiture({self.marque} {self.modele}, {self.annee})"

v1 = class>="cls">Voiture("Toyota", "Corolla", 2018)
v1.afficher_info()
print(f"  Âge : {v1.calculer_age()} ans")
print(v1)         # appelle __str__
Ex 7.2 Classe Rectangle

Trois méthodes + vérification carré avec comparaison des dimensions.

ex_7_2.py
class class>="cls">Rectangle:
    def __init__(self, longueur, largeur):
        self.longueur = longueur
        self.largeur  = largeur

    def calculer_aire(self):
        return self.longueur * self.largeur

    def calculer_perimetre(self):
        return 2 * (self.longueur + self.largeur)

    def est_carre(self):
        return self.longueur == self.largeur

    def __str__(self):
        return f"class>="cls">Rectangle({self.longueur} x {self.largeur})"

r = class>="cls">Rectangle(6, 4)
print(f"  {r}")
print(f"  Aire       : {r.calculer_aire()} cm²")
print(f"  Périmètre  : {r.calculer_perimetre()} cm")
print(f"  Carré ?    : {r.est_carre()}")
print(f"  Carré(5,5) : {class>="cls">Rectangle(5, 5).est_carre()}")
Ex 7.3 Classe Etudiant avec Compteur

Attribut de classe partagé + @classmethod pour y accéder.

ex_7_3.py
class class>="cls">Etudiant:
    nombre_etudiants = 0   # attribut de CLASSE (partagé)

    def __init__(self, nom, prenom):
        self.nom    = nom
        self.prenom = prenom
        self.notes  = []
        class>="cls">Etudiant.nombre_etudiants += 1   # incrémente à chaque création

    def ajouter_note(self, note):
        if 0 <= note <= 20:
            self.notes.append(note)
        else:
            print(f"  ❌ Note {note} invalide.")

    def calculer_moyenne(self):
        return sum(self.notes) / len(self.notes) if self.notes else None

    class>="fn">@classmethod
    def afficher_total(cls):
        print(f"  Total étudiants : {cls.nombre_etudiants}")

    def __str__(self):
        moy = self.calculer_moyenne()
        return f"{self.prenom} {self.nom} — Moy: {moy:.2f}" if moy else f"{self.prenom} {self.nom}"

e1 = class>="cls">Etudiant("Dupont", "Alice")
e1.ajouter_note(15)
e1.ajouter_note(18)
e1.ajouter_note(25)   # refusée
print(e1)
class>="cls">Etudiant.afficher_total()
Ex 7.5 Classe Produit avec Stock

@property pour le getter, @prix.setter pour la validation.

💡
Le préfixe _ signale un attribut protégé par convention (pas d'accès direct).
ex_7_5.py
class class>="cls">Produit:
    def __init__(self, nom, prix, stock=0):
        self.nom    = nom
        self._prix  = prix   # attribut "protégé"
        self._stock = stock

    class>="fn">@property
    def prix(self):              # getter
        return self._prix

    class>="fn">@prix.setter
    def prix(self, valeur):      # setter avec validation
        if valeur <= 0:
            raise class>="cls">ValueError("Le prix doit être positif.")
        self._prix = valeur

    class>="fn">@property
    def stock(self): return self._stock

    def ajouter_stock(self, qte):
        if qte > 0:
            self._stock += qte
            print(f"  ✅ +{qte}. Stock : {self._stock}")

    def vendre(self, qte):
        if qte > self._stock:
            print(f"  ❌ Stock insuffisant ({self._stock} dispo).")
        else:
            self._stock -= qte
            print(f"  ✅ {qte} vendu(s). Restant : {self._stock}")

p = class>="cls">Produit("Téléphone", 150_000, 10)
p.vendre(3)
p.ajouter_stock(5)
print(f"  Prix : {p.prix:,} FCFA | Stock : {p.stock}")
Ex 7.6 Hiérarchie de Véhicules

super().__init__() appelle le constructeur parent avant d'ajouter les attributs enfant.

💡
Le polymorphisme : la méthode appelée dépend du type réel de l'objet, pas du type déclaré.
ex_7_6.py
class class>="cls">Vehicule:
    def __init__(self, marque, vitesse_max):
        self.marque      = marque
        self.vitesse_max = vitesse_max

    def demarrer(self):
        print(f"  Véhicule {self.marque} démarre.")

class class>="cls">Voiture(class>="cls">Vehicule):
    def __init__(self, marque, vitesse_max, nb_portes):
        super().__init__(marque, vitesse_max)   # appel parent
        self.nb_portes = nb_portes

    def demarrer(self):   # override
        print(f"  🚗 {self.marque} ({self.nb_portes} portes) : moteur lancé !")

class class>="cls">Moto(class>="cls">Vehicule):
    def __init__(self, marque, vitesse_max, cylindree):
        super().__init__(marque, vitesse_max)
        self.cylindree = cylindree

    def demarrer(self):   # override
        print(f"  🏍️  {self.marque} {self.cylindree}cc : VROOOOM !")

# Polymorphisme : même appel, comportements différents
for v in [class>="cls">Voiture("Toyota", 180, 4), class>="cls">Moto("Honda", 200, 600), class>="cls">Vehicule("Vélo", 30)]:
    v.demarrer()
Ex 7.7 Classe Fraction

Simplification automatique par le PGCD, surcharge des opérateurs + et *.

💡
gcd(a, b) (from math) retourne le plus grand commun diviseur.
ex_7_7.py
from math import gcd

class class>="cls">Fraction:
    def __init__(self, numerateur, denominateur):
        if denominateur == 0:
            raise class>="cls">ValueError("Dénominateur ne peut pas être zéro.")
        commun = gcd(abs(numerateur), abs(denominateur))
        self.num = numerateur   // commun
        self.den = denominateur // commun
        if self.den < 0:           # signe toujours au numérateur
            self.num, self.den = -self.num, -self.den

    def __str__(self):
        return str(self.num) if self.den == 1 else f"{self.num}/{self.den}"

    def __add__(self, other):
        return class>="cls">Fraction(self.num * other.den + other.num * self.den,
                        self.den * other.den)

    def __mul__(self, other):
        return class>="cls">Fraction(self.num * other.num, self.den * other.den)

    def __eq__(self, other):
        return self.num == other.num and self.den == other.den

f1, f2 = class>="cls">Fraction(1, 2), class>="cls">Fraction(1, 3)
print(f"{f1} + {f2} = {f1 + f2}")    # 1/2 + 1/3 = 5/6
print(f"{f1} * {f2} = {f1 * f2}")    # 1/2 * 1/3 = 1/6
print(f"6/4 = {class>="cls">Fraction(6, 4)}")      # simplifié -> 3/2
8

Modules & NumPy

Modules standards · Créer ses modules · NumPy arrays · Broadcasting

Créer et importer des modules
NumPy arrays et opérations vectorielles
Statistiques descriptives avec NumPy
Indexation booléenne
Ex 8.1 Module de Calculs Personnalisé

Un module Python = un fichier .py avec des fonctions réutilisables.

💡
if __name__ == "__main__" : ce bloc ne s'exécute PAS quand on fait import calculs.
calculs.py
# calculs.py — Sauvegardez ce fichier, puis : import calculs

def factorielle(n):
    """Factorielle de n (n >= 0)."""
    if n < 0: raise class>="cls">ValueError("n doit être >= 0")
    if n == 0: return 1
    resultat = 1
    for i in range(1, n + 1):
        resultat *= i
    return resultat

def pgcd(a, b):
    """PGCD par l&#x27;algorithme d'Euclide."""
    a, b = abs(a), abs(b)
    while b:
        a, b = b, a % b
    return a

def est_premier(n):
    """True si n est premier."""
    if n < 2: return False
    for i in range(2, int(n**0.5) + 1):
        if n % i == 0: return False
    return True

def fibonacci(n):
    """n premiers termes de la suite de Fibonacci."""
    if n <= 0: return []
    if n == 1: return [0]
    s = [0, 1]
    for _ in range(2, n):
        s.append(s[-1] + s[-2])
    return s

if __name__ == "__main__":   # s'exécute uniquement si lancé directement
    print(factorielle(5))    # 120
    print(pgcd(48, 18))      # 6
    print(est_premier(17))   # True
    print(fibonacci(10))     # [0,1,1,2,3,5,8,13,21,34]
Ex 8.3 Statistiques avec NumPy

Génération aléatoire + toutes les fonctions statistiques NumPy en quelques lignes.

💡
L'indexation booléenne notes[notes >= 10] filtre sans boucle — c'est la puissance NumPy.
ex_8_3.py
import numpy as np

notes = np.random.randint(0, 21, 100)   # 100 notes entre 0 et 20

print(f"Moyenne    : {np.mean(notes):.2f}")
print(f"Médiane    : {np.median(notes):.1f}")
print(f"Écart-type : {np.std(notes):.2f}")
print(f"Min / Max  : {np.min(notes)} / {np.max(notes)}")

admis = np.sum(notes >= 10)
print(f"Notes >= 10 : {admis}/100  ({admis}%)")
print(f"  < 10  : {np.sum(notes < 10)}")
print(f"  10-14 : {np.sum((notes >= 10) & (notes < 15))}")
print(f"  >= 15 : {np.sum(notes >= 15)}")
Ex 8.6 Simulation Monte Carlo — Estimer π

Points aléatoires dans un carré [0,1] × [0,1] : proportion dans le quart de cercle ≈ π/4.

💡
Plus n est grand, plus l'estimation est précise — c'est la loi des grands nombres.
ex_8_6.py
import numpy as np

def estimer_pi(n=100_000):
    """Estime pi par la méthode de Monte Carlo."""
    x = np.random.rand(n)
    y = np.random.rand(n)
    dans_cercle = (x**2 + y**2) < 1   # indexation booléenne
    return 4 * np.sum(dans_cercle) / n

print("Estimation de π par Monte Carlo :")
for n in [1_000, 10_000, 100_000, 1_000_000]:
    pi_est = estimer_pi(n)
    erreur = abs(pi_est - np.pi)
    print(f"  n={n:>8,} : π ≈ {pi_est:.6f}  (erreur: {erreur:.6f})")
9

Pandas & Analyse de Données

Series · DataFrames · Filtrage · Groupby · Valeurs manquantes

Créer et explorer des DataFrames
Filtrage avec conditions booléennes
Agrégations avec groupby()
Export CSV/JSON
Ex 9.1 Créer et Explorer un DataFrame

info(), describe(), unique(), value_counts() — les 4 commandes d'exploration.

ex_9_1.py
import pandas as pd

data = {
    "nom"          : ["Alice", "Bob", "Charlie", "Diana", "Eve"],
    "age"          : [22, 25, 21, 23, 24],
    "ville"        : ["Libreville", "Port-Gentil", "Libreville", "Oyem", "Libreville"],
    "note_maths"   : [15, 12, 18, 9, 14],
    "note_physique": [14, 16, 13, 11, 17],
}
df = pd.DataFrame(data)

print("=== info() ===")
df.info()

print("\n=== describe() ===")
print(df.describe())

print("\n=== Villes uniques ===")
print(df["ville"].unique())

print("\n=== Répartition par ville ===")
print(df["ville"].value_counts())
Ex 9.2 Filtrage de Données

Opérateurs booléens & et | pour combiner des conditions de filtrage.

💡
Toujours entourer les conditions combinées de parenthèses : (cond1) & (cond2).
ex_9_2.py
import pandas as pd

data = {
    "nom"         : ["Alice", "Bob", "Charlie", "Diana", "Eve"],
    "note_maths"  : [15, 12, 18, 9, 14],
    "note_physique": [14, 16, 13, 11, 17],
    "note_info"   : [16, 14, 15, 10, 13],
}
df = pd.DataFrame(data)
df["moyenne"] = df[["note_maths", "note_physique", "note_info"]].mean(axis=1)

print("=== Maths > 15 ===")
print(df[df["note_maths"] > 15][["nom", "note_maths"]])

print("\n=== Maths > 15 ET Physique > 15 ===")
masque = (df["note_maths"] > 15) & (df["note_physique"] > 15)
print(df[masque][["nom", "note_maths", "note_physique"]])

print("\n=== Moyenne >= 15 ===")
print(df[df["moyenne"] >= 15][["nom", "moyenne"]].round(2))

print("\n=== Meilleur en Informatique ===")
print(df.loc[df["note_info"].idxmax(), ["nom", "note_info"]])
Ex 9.4 Analyse de Ventes par Groupby

groupby() + agg() pour des analyses multi-colonnes.

💡
idxmax() retourne l'index (= le nom du produit) correspondant à la valeur maximale.
ex_9_4.py
import pandas as pd

data = {
    "produit" : ["Riz","Farine","Riz","Sucre","Farine","Riz","Sucre"],
    "ville"   : ["Lib","Lib","PG","Lib","PG","Lib","PG"],
    "quantite": [50,  30,  40,  25,  20,  60,  15],
    "prix"    : [500, 400, 500, 800, 400, 500, 800],
}
df = pd.DataFrame(data)
df["total"] = df["quantite"] * df["prix"]

print("=== Total par produit ===")
print(df.groupby("produit")["total"].sum().sort_values(ascending=False))

print("\n=== Total par ville ===")
print(df.groupby("ville")["total"].sum())

print("\n=== Quantité moyenne par produit ===")
print(df.groupby("produit")["quantite"].mean().round(1))

produit_top = df.groupby("produit")["quantite"].sum().idxmax()
print(f"\n=== class>="cls">Produit le plus vendu : {produit_top} ===")