Objectifs d’apprentissage¶
À la fin de ce séminaire vous serez capable de :
Comprendre ce qu’est le flux de contrôle et pourquoi les algorithmes en dépendent
Écrire des branches
if/elif/elseet raisonner sur les tables de véritéUtiliser des boucles
foravecrange(),enumerate()etzip()Utiliser des boucles
whileen toute sécurité, et savoir quand les préférer auxforContrôler l’exécution des boucles avec
break,continueetpassReconnaître la complexité des boucles imbriquées (informellement : O(n²))
Écrire des list comprehensions basiques
Partie 1 : Théorie¶
2.1 Qu’est-ce que le flux de contrôle ?¶
Par défaut, Python exécute les instructions de haut en bas, les unes après les autres. Le flux de contrôle modifie cet ordre par défaut : il permet au programme de prendre des décisions, de répéter des actions et d’ignorer du code.
Tout algorithme se réduit finalement à :
Séquence — faire A, puis B, puis C
Sélection — si la condition X est vraie, faire A ; sinon faire B
Itération — répéter A jusqu’à ce que la condition Y soit satisfaite
Ces trois constructions suffisent pour exprimer n’importe quelle fonction calculable (c’est ce qu’on appelle la complétude de Turing).
Pourquoi est-ce important pour les algorithmes ?¶
La structure de votre flux de contrôle détermine directement :
Correction — l’algorithme produit-il la bonne réponse ?
Efficacité — combien d’opérations effectue-t-il ?
Par exemple, la recherche linéaire parcourt chaque élément (une boucle), tandis que la recherche binaire divise l’espace de recherche par deux à chaque étape (boucle + branchement). Même problème, performances radicalement différentes.
Control Flow at a Glance
─────────────────────────────────────────────────
Sequence: A → B → C
Selection: condition? → A (yes) / B (no)
Iteration: condition? → A → back to condition
─────────────────────────────────────────────────2.2 if / elif / else — Branching¶
Syntaxe¶
if condition_1:
# executed when condition_1 is True
elif condition_2:
# executed when condition_1 is False AND condition_2 is True
else:
# executed when ALL conditions above are FalseLes clauses
elifetelsesont optionnelles.Vous pouvez avoir plusieurs clauses
elif.Les conditions sont toute expression qui évalue à une valeur truthy ou falsy.
Truthiness en Python¶
Falsy (traité comme False) | Truthy (traité comme True) |
|---|---|
False, None | True |
0, 0.0 | Tout nombre non nul |
"", '' | Toute chaîne non vide |
[], (), {}, set() | Tout conteneur non vide |
Opérateurs de comparaison¶
| Opérateur | Signification |
|---|---|
== | Égal à |
!= | Différent de |
<, > | Inférieur / supérieur à |
<=, >= | Inférieur / supérieur ou égal à |
is | Identité (même objet en mémoire) |
in | Test d’appartenance |
Opérateurs booléens : table de vérité¶
A | B | A and B | A or B | not A |
|---|---|---|---|---|
| T | T | T | T | F |
| T | F | F | T | F |
| F | T | F | T | T |
| F | F | F | F | T |
# --- Basic if/elif/else ---
def classify_grade(score):
"""Return a letter grade for a score in [0, 100]."""
if score >= 90:
return "A"
elif score >= 80:
return "B"
elif score >= 70:
return "C"
elif score >= 60:
return "D"
else:
return "F"
scores = [95, 83, 71, 64, 50, 100, 0]
for s in scores:
print(f"Score {s:>3} → Grade {classify_grade(s)}")
print()
# --- Chained comparisons (Python-specific, very readable) ---
x = 15
if 10 <= x <= 20: # equivalent to: 10 <= x and x <= 20
print(f"{x} is between 10 and 20 (inclusive)")
# --- Ternary / conditional expression (one-liner if/else) ---
temperature = 22
weather = "warm" if temperature >= 20 else "cold"
print(f"Temperature {temperature}°C is {weather}")# --- Interactive demo: which branch executes? ---
import ipywidgets as widgets
from IPython.display import display, HTML
def show_branch(number):
"""Show which if/elif/else branch is taken for a given number."""
output_lines = [f"<b>Input number: {number}</b><br>"]
output_lines.append("<pre style='font-family:monospace; font-size:13px;'>")
# We manually trace through each condition
if number > 0:
branch = "POSITIVE"
colour = "#2ecc71"
elif number < 0:
branch = "NEGATIVE"
colour = "#e74c3c"
else:
branch = "ZERO"
colour = "#3498db"
# Build a visual "code trace"
conditions = [
(f"if {number} > 0:", number > 0),
(f"elif {number} < 0:", number < 0),
("else:", not (number > 0 or number < 0)),
]
for code_line, taken in conditions:
marker = " ← TAKEN" if taken else ""
style = f"color:{colour}; font-weight:bold;" if taken else "color:#888;"
output_lines.append(
f"<span style='{style}'>{code_line}{marker}</span><br>"
)
output_lines.append("</pre>")
output_lines.append(
f"<p style='color:{colour}; font-size:15px;'>Result: <b>{branch}</b></p>"
)
display(HTML("".join(output_lines)))
slider = widgets.IntSlider(
value=0, min=-20, max=20, step=1,
description="Number:",
style={"description_width": "initial"},
layout=widgets.Layout(width="55%")
)
out = widgets.interactive_output(show_branch, {"number": slider})
display(widgets.Label("Move the slider and watch which branch is taken:"), slider, out)2.3 for Loops — Itération définie¶
Une boucle for itère sur n’importe quel itérable (liste, chaîne, range, dict, fichier, …). Vous savez à l’avance (conceptuellement) combien d’itérations auront lieu.
range()¶
| Appel | Valeurs produites |
|---|---|
range(5) | 0, 1, 2, 3, 4 |
range(2, 7) | 2, 3, 4, 5, 6 |
range(0, 10, 2) | 0, 2, 4, 6, 8 |
range(5, 0, -1) | 5, 4, 3, 2, 1 |
enumerate() — boucle avec indice¶
Utilisez enumerate() au lieu de range(len(...)) — c’est plus Pythonique et moins sujet aux erreurs.
# BAD (C-style, avoid in Python):
for i in range(len(items)):
print(items[i])
# GOOD:
for i, item in enumerate(items):
print(i, item)zip() — itérer sur plusieurs itérables¶
names = ["Alice", "Bob"]
scores = [95, 87]
for name, score in zip(names, scores):
print(f"{name}: {score}")# --- range() examples ---
print("range(5):", list(range(5)))
print("range(2, 7):", list(range(2, 7)))
print("range(0,10,2):", list(range(0, 10, 2)))
print("range(5,0,-1):", list(range(5, 0, -1)))
print()
# --- Iterating over a list ---
fruits = ["apple", "banana", "cherry", "date"]
print("Direct iteration:")
for fruit in fruits:
print(f" {fruit}")
print()
# --- enumerate() ---
print("With enumerate():")
for index, fruit in enumerate(fruits):
print(f" [{index}] {fruit}")
# enumerate() with a start index
print("\nStarting from index 1:")
for rank, fruit in enumerate(fruits, start=1):
print(f" #{rank}: {fruit}")
print()
# --- zip() ---
names = ["Alice", "Bob", "Carol"]
scores = [95, 87, 72]
grades = ["A", "B", "C"]
print("With zip():")
for name, score, grade in zip(names, scores, grades):
print(f" {name:>6}: {score} ({grade})")
print()
# --- Iterating over a string (strings are iterables!) ---
word = "Python"
print(f"Letters in '{word}': ", end="")
for ch in word:
print(ch, end=" ")
print()
# --- Iterating over a dict ---
capitals = {"France": "Paris", "Japan": "Tokyo", "Brazil": "Brasília"}
print("\nCountry capitals:")
for country, capital in capitals.items():
print(f" {country}: {capital}")2.4 while Loops — Itération indéfinie¶
Utilisez une boucle while lorsque vous ne savez pas à l’avance combien d’itérations sont nécessaires — seule la condition d’arrêt importe.
while condition:
# body — runs as long as condition is TrueQuand choisir while plutôt que for¶
| Situation | À utiliser |
|---|---|
| Nombre connu d’itérations | for |
| Itérer sur une collection | for |
| Attente d’une entrée utilisateur / événement | while |
| Algorithme qui converge (ex. méthode de Newton) | while |
| Boucle de jeu | while |
Danger : boucles infinies¶
Si la condition ne devient jamais False, la boucle tourne indéfiniment. Assurez-vous toujours que :
La variable de boucle est mise à jour à l’intérieur du corps.
La condition sera finalement satisfaite.
Vous avez un
breakcomme sortie d’urgence si nécessaire.
import math
# --- Basic while loop ---
counter = 0
while counter < 5:
print(f" counter = {counter}")
counter += 1 # IMPORTANT: increment to avoid infinite loop
print("Loop finished.")
print()
# --- Collatz conjecture: a famous sequence ---
# Start with any positive integer n.
# If n is even: divide by 2. If odd: multiply by 3 and add 1.
# Conjecture: it always reaches 1 (unproven for all numbers!).
def collatz(n):
"""Return the Collatz sequence starting at n."""
sequence = [n]
while n != 1:
if n % 2 == 0:
n = n // 2
else:
n = 3 * n + 1
sequence.append(n)
return sequence
for start in [6, 11, 27]:
seq = collatz(start)
print(f"Collatz({start}): {seq}")
print(f" Steps to reach 1: {len(seq) - 1}")
print()
# --- Newton's method: square root approximation ---
# Uses while loop because convergence time is not known in advance.
def sqrt_newton(n, tolerance=1e-10):
"""Approximate sqrt(n) using Newton-Raphson iteration."""
if n < 0:
raise ValueError("Cannot take square root of a negative number")
guess = n / 2.0 # initial guess
iterations = 0
while abs(guess * guess - n) > tolerance:
guess = (guess + n / guess) / 2 # Newton step
iterations += 1
return guess, iterations
for num in [2, 9, 100, 12345]:
approx, iters = sqrt_newton(num)
print(f"sqrt({num:>5}): approx={approx:.8f}, math.sqrt={math.sqrt(num):.8f}, iters={iters}")2.5 break, continue, pass¶
| Instruction | Effet |
|---|---|
break | Sort immédiatement de la boucle la plus interne |
continue | Saute le reste de l’itération courante ; passe à la suivante |
pass | Ne fait rien — un placeholder syntaxique (bloc vide) |
Clause else sur les boucles¶
Python a une caractéristique particulière : les boucles for/while peuvent avoir une clause else qui s’exécute seulement si la boucle s’est terminée normalement (c’est-à-dire qu’elle n’a pas été interrompue par un break).
for item in collection:
if condition:
break # skip the else
else:
# runs only if break was never hit
print("No item matched")Ceci est très utile pour les algorithmes de recherche.
# --- break: stop the loop early ---
print("=== break ===")
for i in range(10):
if i == 5:
print(f" Found 5! Breaking out of loop.")
break
print(f" i = {i}")
print()
# --- continue: skip even numbers ---
print("=== continue (printing only odd numbers) ===")
for i in range(10):
if i % 2 == 0:
continue # skip even
print(f" {i}")
print()
# --- pass: placeholder in an empty branch ---
print("=== pass ===")
for i in range(5):
if i == 2:
pass # placeholder — do nothing for 2 (no error without a body)
else:
print(f" Processing {i}")
print()
# --- for/else: prime checking ---
print("=== for...else: prime checking ===")
def is_prime(n):
"""Return True if n is prime, False otherwise."""
if n < 2:
return False
for divisor in range(2, int(n ** 0.5) + 1):
if n % divisor == 0:
return False # found a divisor — not prime
return True # loop completed without break → prime
primes = [n for n in range(2, 30) if is_prime(n)]
print(f" Primes below 30: {primes}")
print()
# Version explicitly using for/else:
def is_prime_explicit(n):
"""Same as above but uses for...else explicitly to illustrate the pattern."""
if n < 2:
return False
for divisor in range(2, int(n ** 0.5) + 1):
if n % divisor == 0:
break # composite — break skips the else
else:
return True # else only runs if no break occurred
return False
test_nums = [2, 7, 9, 13, 25, 29]
for num in test_nums:
print(f" is_prime({num}) = {is_prime_explicit(num)}")2.6 Boucles imbriquées et complexité¶
Une boucle à l’intérieur d’une autre boucle s’appelle une boucle imbriquée. Elles sont nécessaires pour travailler avec des données 2D (matrices, grilles) ou pour certains algorithmes (tri à bulles, multiplication de matrices).
Informellement : O(n²)¶
Si la boucle externe et la boucle interne exécutent chacune n itérations, le nombre total d’opérations est approximativement n × n = n². On l’écrit O(n²) (notation Big O — vue formellement plus tard dans le cours).
| n | n (boucle simple) | n² (boucle imbriquée) |
|---|---|---|
| 10 | 10 | 100 |
| 100 | 100 | 10 000 |
| 1 000 | 1 000 | 1 000 000 |
| 10 000 | 10 000 | 100 000 000 |
C’est pourquoi des algorithmes comme tri à bulles (O(n²)) deviennent peu pratiques pour de grandes entrées, tandis que tri fusion (O(n log n)) s’adapte bien mieux.
# --- Nested loop: 5×5 multiplication table ---
print("5 × 5 Multiplication Table:")
print(" ", end="")
for j in range(1, 6):
print(f"{j:>4}", end="")
print()
print(" " + "-" * 20)
for i in range(1, 6): # outer loop: rows
print(f"{i:>2} |", end="")
for j in range(1, 6): # inner loop: columns
print(f"{i * j:>4}", end="")
print() # newline after each row
print()
# --- Count operations to visualise O(n²) growth ---
import matplotlib.pyplot as plt
ns = list(range(1, 51))
ops_linear = ns # O(n)
ops_quadratic = [n ** 2 for n in ns] # O(n²)
ops_nlogn = [n * (n.bit_length()) for n in ns] # rough O(n log n)
fig, ax = plt.subplots(figsize=(8, 4))
ax.plot(ns, ops_linear, label="O(n) — linear", linewidth=2)
ax.plot(ns, ops_nlogn, label="O(n log n) — merge sort", linewidth=2, linestyle="--")
ax.plot(ns, ops_quadratic, label="O(n²) — nested loop", linewidth=2, linestyle=":")
ax.set_xlabel("Input size n")
ax.set_ylabel("Number of operations (approx.)")
ax.set_title("Growth of Algorithm Complexity", fontweight="bold")
ax.legend()
ax.set_xlim(1, 50)
ax.set_ylim(0)
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
plt.tight_layout()
plt.show()2.7 List Comprehensions — Itération concise¶
Une list comprehension crée une nouvelle liste en appliquant une expression à chaque élément d’un itérable, avec un filtrage optionnel :
[expression for variable in iterable if condition]Équivalente à une boucle for avec append(), mais plus concise et souvent plus rapide.
# Classic loop approach:
squares = []
for x in range(10):
squares.append(x ** 2)
# List comprehension (equivalent, preferred in Python):
squares = [x ** 2 for x in range(10)]Règle empirique : si la comprehension tient sur une seule ligne et reste lisible, utilisez-la. Si elle nécessite plusieurs lignes ou une logique complexe, utilisez une boucle classique.
# --- Basic list comprehension ---
squares = [x ** 2 for x in range(1, 11)]
print(f"Squares 1..10: {squares}")
# --- With condition (filter) ---
even_squares = [x ** 2 for x in range(1, 11) if x % 2 == 0]
print(f"Even squares: {even_squares}")
# --- Transforming strings ---
words = ["hello", "world", "python", "algorithmics"]
upper_long = [w.upper() for w in words if len(w) > 5]
print(f"Uppercased long words: {upper_long}")
# --- Flattening a 2D list ---
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [elem for row in matrix for elem in row]
print(f"Flattened matrix: {flat}")
# --- Dictionary comprehension (bonus) ---
word_lengths = {word: len(word) for word in words}
print(f"Word lengths: {word_lengths}")
# --- Set comprehension (bonus) ---
unique_lengths = {len(word) for word in words}
print(f"Unique lengths: {unique_lengths}")
# --- Performance note ---
# Comprehensions are generally faster than equivalent loops because
# the iteration is optimised at the C level in CPython.
import timeit
loop_time = timeit.timeit(
"result = []\nfor x in range(1000):\n result.append(x**2)",
number=10000
)
comp_time = timeit.timeit(
"result = [x**2 for x in range(1000)]",
number=10000
)
print(f"\nPerformance (10000 runs of squaring 1000 elements):")
print(f" for loop: {loop_time:.4f}s")
print(f" list comprehension:{comp_time:.4f}s")
print(f" Speedup: {loop_time / comp_time:.2f}x")Exercice 1 : Maximum de trois nombres¶
Écrivez une fonction max_of_three(a, b, c) qui retourne le plus grand des trois nombres en utilisant if/elif/else (n’utilisez pas la fonction intégrée max()).
Ceci est l’équivalent Python de l’exercice C largest_num.c.
# Exercise 1 — Maximum of three numbers
# Your code goes hereExercice 2 : FizzBuzz¶
Question classique d’entretien. Affichez les nombres de 1 à n, mais :
Affichez “Fizz” pour les multiples de 3
Affichez “Buzz” pour les multiples de 5
Affichez “FizzBuzz” pour les multiples à la fois de 3 et de 5
Affichez le nombre lui-même sinon
Important : vérifiez d’abord la condition combinée (FizzBuzz), sinon elle ne sera jamais atteinte.
# Exercise 2 — FizzBuzz
# Your code goes hereExercice 3 : Vérificateur d’année bissextile¶
Une année est bissextile si :
Elle est divisible par 4 ET
Elle n’est pas divisible par 100, sauf si elle est aussi divisible par 400.
Exemples : 2000 ✓, 1900 ✗, 2024 ✓, 2023 ✗
Écrivez is_leap_year(year) et testez-la.
# Exercise 3 — Leap year checker
Exercice 4 : Calculatrice interactive (boucle while)¶
Créez une calculatrice qui demande de manière répétée à l’utilisateur deux nombres et un opérateur, puis affiche le résultat. La boucle continue jusqu’à ce que l’utilisateur tape quit.
Opérations supportées : +, -, *, /
# Exercise 4 — Interactive calculator with while loop
Exercice 5 : Table de multiplication¶
Écrivez une fonction multiplication_table(n) qui affiche la table de multiplication pour un nombre donné n (de 1 × n à 12 × n).
Extension : affichez une grille complète n × n.
# Exercise 5 — Multiplication table
Récapitulatif¶
| Concept | Quand utiliser |
|---|---|
if/elif/else | Prise de décision basée sur des conditions |
for | Nombre d’itérations connu / itérer sur une collection |
while | Nombre d’itérations inconnu, piloté par événements, convergence |
break | Sortir prématurément d’une boucle (recherche trouvée / erreur) |
continue | Ignorer l’élément courant, continuer avec le suivant |
pass | Placeholder syntaxique pour blocs vides |
| List comprehension | Création concise de listes à partir d’une itération |
Prochain séminaire : Structures de données — listes, tuples, dictionnaires, ensembles, fonctions et classes.