Guide des Indices — Python
3 niveaux par exercice · Erreurs classiques · Tests de validation · Questions de compréhension
Règle absolue
→ Lis l'indice 1. Si bloqué 15 min → indice 2. Si bloqué 30 min → indice 3.
→ Ne jamais lire l'indice 3 en premier — ça annule tout l'apprentissage.
SEMAINE 1 — Bases du langage
Exercice 1.1 — Calculatrice basique
INDICES
Indice 1 input() retourne TOUJOURS une string — '5' + '3' = '53' pas 8. Comment convertir une string
en nombre ?
Indice 2 float(input('Entrez un nombre: ')) fait les deux en une ligne. Pour la division par zéro, if b == 0
avant de diviser.
Indice 3 a = float(input('Nombre 1: '))
b = float(input('Nombre 2: '))
print(f'Somme: {a + b}')
if b != 0: print(f'Division: {a / b}')
else: print('Division par zéro impossible')
Erreurs classiques
→ Oublier de convertir input() → TypeError sur les opérations
→ Utiliser int() au lieu de float() → 5/2 = 2 au lieu de 2.5
→ f-string mal formé : f'résultat: result' au lieu de f'résultat: {result}'
Test de validation
→ Entrée 10 et 3 → somme=13.0, diff=7.0, produit=30.0, quotient=3.333...
→ Entrée 5 et 0 → affiche le message d'erreur, pas de crash
Question de compréhension
→ Pourquoi input() retourne toujours une string même si l'utilisateur tape un nombre ? Que se passe-t-il si
tu ne convertis pas ?
Exercice 1.2 — Convertisseur de températures
INDICES
Indice 1 La formule F = C * 9/5 + 32. En Python, 9/5 = 1.8. Vérifie : 0°C = 32°F et 100°C = 212°F.
Indice 2 Pour afficher 2 décimales : f'{valeur:.2f}' dans le f-string. Exemple : f'Fahrenheit:
{fahrenheit:.2f}°F'
Indice 3 celsius = float(input('Celsius: '))
fahrenheit = celsius * 9/5 + 32
kelvin = celsius + 273.15
print(f'{celsius}°C = {fahrenheit:.2f}°F = {kelvin:.2f}K')
Erreurs classiques
→ Écrire 9/5 en entiers : en Python 3 c'est 1.8 — OK. En Python 2 c'était 1 — attention si tu vois du vieux
code
→ Oublier les parenthèses : celsius * 9/5 + 32 ≠ celsius * (9/5 + 32)
Test de validation
→ 0°C → 32.00°F → 273.15K
→ 100°C → 212.00°F → 373.15K
→ -40°C → -40.00°F (cas particulier)
Question de compréhension
→ Que retourne type(input()) ? Pourquoi est-ce important de le savoir avant d'écrire la moindre ligne de
code ?
Exercice 1.3 — Détecteur de type
INDICES
Indice 1 isinstance(val, int) retourne True si val est un entier. Mais True et False sont aussi des int en
Python (bool hérite de int). Quel ordre de vérification s'impose ?
Indice 2 Tester isinstance(val, bool) EN PREMIER, avant isinstance(val, int). Sinon True sera détecté
comme 'entier' au lieu de 'booléen'.
Indice 3 def detect_type(val):
if isinstance(val, bool): return 'booléen'
if isinstance(val, int): return 'entier'
if isinstance(val, float): return 'flottant'
if isinstance(val, str): return 'string'
return 'autre'
Erreurs classiques
→ Tester int avant bool → True retourne 'entier' au lieu de 'booléen'
→ Utiliser type(val) == int au lieu de isinstance() — ne gère pas l'héritage
Test de validation
→ detect_type(42) → 'entier'
→ detect_type(True) → 'booléen' (PAS 'entier')
→ detect_type(3.14) → 'flottant'
→ detect_type('hi') → 'string'
Question de compréhension
→ Pourquoi bool est-il une sous-classe de int en Python ? Que retourne isinstance(True, int) ?
Exercice 2.1 — FizzBuzz
INDICES
Indice 1 L'opérateur % donne le reste de la division. 15 % 3 = 0, 15 % 5 = 0. Pour tester si un nombre
est divisible par 3 ET 5, quelle condition écrire ?
Indice 2 L'ordre des if/elif est crucial : tester d'abord if n % 3 == 0 and n % 5 == 0, PUIS les cas
simples. Si tu mets les cas simples en premier, FizzBuzz ne sera jamais atteint.
Indice 3 for i in range(1, 101):
if i % 3 == 0 and i % 5 == 0:
print('FizzBuzz')
elif i % 3 == 0:
print('Fizz')
elif i % 5 == 0:
print('Buzz')
else:
print(i)
Erreurs classiques
→ Tester Fizz et Buzz avant FizzBuzz → les multiples de 15 affichent 'Fizz' au lieu de 'FizzBuzz'
→ range(1, 100) au lieu de range(1, 101) — oublie le 100
Test de validation
→ Ligne 15 → FizzBuzz
→ Ligne 30 → FizzBuzz
→ Ligne 9 → Fizz
→ Ligne 10 → Buzz
→ Ligne 7 → 7
Question de compréhension
→ Pourquoi l'ordre des conditions if/elif change le résultat ? Que se passerait-il si tu mettais le cas
FizzBuzz EN DERNIER ?
Exercice 2.3 — Devine le nombre
INDICES
Indice 1 [Link](1, 100) génère un entier entre 1 et 100 inclus. Pour boucler indéfiniment
jusqu'à la bonne réponse, quelle structure de boucle utiliser ?
Indice 2 while True crée une boucle infinie. break en sort. La structure : while True: → lire la guess →
if guess == secret: print('Bravo') et break → sinon afficher 'Trop grand' ou 'Trop petit'.
Indice 3 import random
secret = [Link](1, 100)
essais = 0
while True:
guess = int(input('Devine: '))
essais += 1
if guess == secret: print(f'Bravo en {essais} essais!'); break
elif guess > secret: print('Trop grand')
else: print('Trop petit')
Erreurs classiques
→ Oublier de convertir input() en int → comparaison string vs int toujours False
→ Ne pas incrémenter le compteur d'essais
→ Oublier break → boucle infinie même après la bonne réponse
Test de validation
→ Deviner le bon nombre → affiche le nombre d'essais
→ Entrer trop grand → 'Trop grand'
→ Entrer trop petit → 'Trop petit'
Question de compréhension
→ Pourquoi 7 essais sont-ils toujours suffisants pour trouver un nombre entre 1 et 100 ? Quel algorithme
optimal utiliser ?
Exercice 3.2 — Palindrome et anagramme
INDICES
Indice 1 Un palindrome se lit pareil dans les deux sens. 'racecar' inversé = 'racecar'. Comment
inverser une string en Python en une seule expression ?
Indice 2 s[::-1] inverse une string. Pour is_palindrome, normalise d'abord : [Link]().replace(' ', '').
Pour is_anagram, trier les lettres des deux strings et comparer.
Indice 3 def is_palindrome(s):
s = [Link]().replace(' ', '')
return s == s[::-1]
def is_anagram(s1, s2):
return sorted([Link]()) == sorted([Link]())
Erreurs classiques
→ Ne pas normaliser la casse → 'Racecar' n'est pas reconnu comme palindrome
→ Pour anagramme : comparer les strings directement au lieu de les trier
Test de validation
→ is_palindrome('racecar') → True
→ is_palindrome('A man a plan a canal Panama') → True
→ is_anagram('listen', 'silent') → True
→ is_anagram('hello', 'world') → False
Question de compréhension
→ Quelle est la complexité de is_anagram avec sorted() ? Existe-t-il une approche O(n) avec un
dictionnaire ?
SEMAINE 2 — Structures de données
Exercice 4.1 — Manipulation de listes
INDICES
Indice 1 ft_flatten : parcourir la liste externe, pour chaque sous-liste, ajouter chaque élément au
résultat. Deux boucles imbriquées suffisent.
Indice 2 ft_unique : parcourir la liste, n'ajouter un élément au résultat QUE S'IL N'Y EST PAS DÉJÀ.
Comment vérifier si un élément est dans une liste ?
Indice 3 def ft_flatten(lst):
result = []
for sublist in lst:
for item in sublist:
[Link](item)
return result
def ft_unique(lst):
seen = []
return [x for x in lst if x not in seen and not [Link](x)]
Erreurs classiques
→ ft_unique avec set() perd l'ordre — utiliser une liste seen à la place
→ ft_chunk : oublier le dernier morceau si len(lst) n'est pas divisible par n
Test de validation
→ ft_flatten([[1,2],[3,4],[5]]) → [1,2,3,4,5]
→ ft_unique([1,2,2,3,1,4]) → [1,2,3,4]
→ ft_chunk([1,2,3,4,5], 2) → [[1,2],[3,4],[5]]
Question de compréhension
→ Pourquoi utiliser une liste seen au lieu d'un set pour ft_unique ? Quelle est la différence de complexité
?
Exercice 5.1 — Compteur de mots
INDICES
Indice 1 Parcourir les mots d'un texte : [Link]().split() donne une liste de mots. Mais 'hello,' et
'hello' sont différents — comment enlever la ponctuation ?
Indice 2 import string — [Link] contient tous les caractères de ponctuation. Pour chaque
mot : [Link]([Link]). Puis compter avec un dict : counts[mot] = [Link](mot,
0) + 1
Indice 3 import string
def word_count(text):
words = [Link]().split()
counts = {}
for word in words:
word = [Link]([Link])
if word: # ignorer les strings vides
counts[word] = [Link](word, 0) + 1
return dict(sorted([Link](), key=lambda x: x[1],
reverse=True))
Erreurs classiques
→ Ne pas enlever la ponctuation → 'hello' et 'hello,' comptent séparément
→ KeyError si on fait counts[mot] += 1 sans initialiser à 0 — utiliser .get()
Test de validation
→ 'hello world hello' → {'hello': 2, 'world': 1}
→ Texte avec ponctuation → les mots comptés sans ponctuation
Question de compréhension
→ Quelle est la différence entre [Link](key, 0) et dict[key] ? Dans quel cas chacun lève une erreur ?
Exercice 8.1 — Classe Etudiant
INDICES
Indice 1 Une classe Python commence par class Etudiant:. __init__(self, nom, age) est le
constructeur — appelé automatiquement à la création. [Link] = [] crée une liste vide pour
chaque instance.
Indice 2 get_average() : sum([Link]) / len([Link]). Mais que faire si notes est vide ? Diviser par
0 plante. Vérifier len([Link]) > 0 avant.
Indice 3 class Etudiant:
def __init__(self, nom, age):
[Link] = nom
[Link] = age
[Link] = []
def add_note(self, note):
if 0 <= note <= 20:
[Link](note)
def get_average(self):
if not [Link]: return 0
return sum([Link]) / len([Link])
def __str__(self):
return f'Etudiant: {[Link]}, Moyenne:
{self.get_average():.1f}'
Erreurs classiques
→ Oublier self. devant les attributs → variables locales perdues après __init__
→ get_average() avec liste vide → ZeroDivisionError
→ Oublier __str__ → print(etudiant) affiche <__main__.Etudiant object at 0x...>
Test de validation
→ e = Etudiant('Alice', 20) → [Link] == 'Alice'
→ e.add_note(15) ; e.add_note(18) → e.get_average() == 16.5
→ print(e) → affiche le nom et la moyenne
Question de compréhension
→ Que signifie self dans une méthode Python ? Pourquoi est-il obligatoire comme premier paramètre de
chaque méthode ?
Exercice 9.1 — Classiques récursifs
INDICES
Indice 1 Toute fonction récursive a un cas de base (quand s'arrêter) et un cas récursif (s'appeler avec
un problème plus petit). Pour factorial, quel est le plus petit problème ?
Indice 2 factorial(0) = 1 (cas de base). factorial(n) = n * factorial(n-1). sum_digits(n) : le dernier chiffre
= n % 10, le reste = n // 10.
Indice 3 def factorial(n):
if n <= 0: return 1 # cas de base
return n * factorial(n - 1)
def fibonacci(n):
if n == 0: return 0
if n == 1: return 1
return fibonacci(n-1) + fibonacci(n-2)
def sum_digits(n):
if n < 10: return n # cas de base
return n % 10 + sum_digits(n // 10)
Erreurs classiques
→ Oublier le cas de base → RecursionError (stack overflow Python)
→ fibonacci sans les 2 cas de base → crash pour n=0 ou n=1
→ Ne pas retourner le résultat de l'appel récursif → retourne None
Test de validation
→ factorial(5) → 120
→ fibonacci(10) → 55
→ sum_digits(123) → 6
→ reverse_string('hello') → 'olleh'
Question de compréhension
→ Combien d'appels fibonacci(5) génère-t-il ? Dessine l'arbre d'appels et compte. Pourquoi fibonacci(40)
est-il si lent ?
Exercice 10.1 — Tris O(n²)
INDICES
Indice 1 Bubble sort : comparer tab[j] et tab[j+1] pour chaque paire adjacente. Si tab[j] > tab[j+1], les
échanger. Répéter n-1 fois. Après chaque passe, le plus grand élément est à sa place.
Indice 2 Pour échanger deux éléments en Python : a, b = b, a (pas besoin de variable tmp !). Pour ne
pas modifier la liste originale, travailler sur [Link]().
Indice 3 def bubble_sort(lst):
tab = [Link]()
n = len(tab)
for i in range(n - 1):
for j in range(n - 1 - i):
if tab[j] > tab[j + 1]:
tab[j], tab[j+1] = tab[j+1], tab[j] # swap
Pythonique
return tab
Erreurs classiques
→ Modifier la liste originale au lieu de travailler sur une copie
→ range(n) au lieu de range(n-1) dans la boucle interne → IndexError sur tab[j+1]
→ Utiliser une variable tmp pour le swap — inutile en Python : a, b = b, a
Test de validation
→ bubble_sort([5,3,8,1,9,2]) → [1,2,3,5,8,9]
→ La liste originale doit rester inchangée après l'appel
Question de compréhension
→ Pourquoi bubble_sort est O(n²) ? Combien de comparaisons fait-il pour une liste de 10 éléments ? De
100 ?
SEMAINE 5 — Web & Automatisation
Exercice 12.1 — Première API REST
INDICES
Indice 1 Flask est un micro-framework web. from flask import Flask; app = Flask(__name__). Une
route = un décorateur + une fonction. @[Link]('/contacts') def get_contacts(): ...
Indice 2 jsonify() convertit un dict ou une liste Python en réponse JSON. [Link] lit le corps
JSON d'une requête POST. Les méthodes HTTP : GET lit, POST crée, DELETE supprime.
Indice 3 from flask import Flask, jsonify, request
app = Flask(__name__)
contacts = []
@[Link]('/contacts', methods=['GET'])
def get_contacts():
return jsonify(contacts)
@[Link]('/contacts', methods=['POST'])
def add_contact():
data = [Link]
[Link](data)
return jsonify(data), 201
if __name__ == '__main__':
[Link](debug=True)
Erreurs classiques
→ Oublier methods=['GET','POST'] → Method Not Allowed 405
→ Retourner un dict directement sans jsonify → TypeError
→ Oublier if __name__ == '__main__': → [Link]() s'exécute à l'import
Test de validation
→ GET /contacts → [] (liste vide au départ)
→ POST /contacts avec {"nom": "Alice"} → 201 Created
→ GET /contacts → [{"nom": "Alice"}]
Question de compréhension
→ Quelle est la différence entre GET et POST ? Pourquoi retourner 201 pour une création et 200 pour
une lecture ?