Cours Complet Python — Bases à la POO
Chapitre 1 — Les bases du langage
1.1 Variables et types de données
Une variable est une boîte qui stocke une valeur. En Python, pas besoin de déclarer le type, il
est détecté automatiquement.
python
nom = "Mathis" # str → texte
age = 20 # int → entier
taille = 1.80 # float → décimal
majeur = True # bool → True / False
rien = None # NoneType → absence de valeur
Tu peux vérifier le type avec type() :
python
print(type(nom)) # <class 'str'>
print(type(age)) # <class 'int'>
Et convertir d'un type à l'autre :
python
age_texte = str(age) # "20"
nombre = int("42") # 42
decimal = float("3.14") # 3.14
1.2 Opérateurs
Arithmétiques :
python
print(10 + 3) # 13 → addition
print(10 - 3) # 7 → soustraction
print(10 * 3) # 30 → multiplication
print(10 / 3) # 3.333... → division réelle
print(10 // 3) # 3 → division entière (arrondi bas)
print(10 % 3) # 1 → modulo (reste de la division)
print(2 ** 8) # 256 → puissance
Comparaison (retournent True ou False) :
python
print(5 == 5) # True → égalité
print(5 != 3) # True → différence
print(5 > 3) # True → supérieur
print(5 <= 5) # True → inférieur ou égal
Logiques :
python
print(True and False) # False → les deux doivent être True
print(True or False) # True → au moins un True suffit
print(not True) # False → inverse
1.3 Affichage et saisie
python
# Affichage simple
print("Bonjour !")
print("Valeur :", 42)
# F-strings (la façon moderne et recommandée)
nom = "Mathis"
age = 20
print(f"Je m'appelle {nom} et j'ai {age} ans.")
# → Je m'appelle Mathis et j'ai 20 ans.
# Tu peux faire des calculs dans les f-strings
print(f"Dans 10 ans j'aurai {age + 10} ans.")
# Saisie utilisateur (retourne toujours une str !)
prenom = input("Ton prénom : ")
annee = int(input("Ton année de naissance : ")) # convertir en int
Exercice 1 — Présentation
Écris un programme qui :
1. Demande à l'utilisateur son prénom, son âge et sa ville.
2. Calcule son année de naissance approximative (2026 - âge).
3. Affiche une phrase du type : "Bonjour Mathis ! Tu habites à Lyon, tu es né(e) vers
2006 et dans 10 ans tu auras 30 ans."
Solution :
python
prenom = input("Ton prénom : ")
age = int(input("Ton âge : "))
ville = input("Ta ville : ")
annee_naissance = 2026 - age
age_dans_10_ans = age + 10
print(f"Bonjour {prenom} ! Tu habites à {ville}, "
f"tu es né(e) vers {annee_naissance} "
f"et dans 10 ans tu auras {age_dans_10_ans} ans.")
Chapitre 2 — Structures de contrôle
2.1 Conditions — if / elif / else
python
note = 14
if note >= 16:
print("Très bien")
elif note >= 14:
print("Bien")
elif note >= 10:
print("Passable")
else:
print("Insuffisant")
Python utilise l'indentation (4 espaces) pour délimiter les blocs. Pas d'accolades comme
en C ou Java.
Tu peux aussi écrire une condition en une ligne (ternaire) :
python
statut = "majeur" if age >= 18 else "mineur"
print(statut)
2.2 Boucle for
La boucle for parcourt une séquence (liste, chaîne, range...) :
python
# range(n) → génère 0, 1, 2, ..., n-1
for i in range(5):
print(i) # 0, 1, 2, 3, 4
# range(début, fin, pas)
for i in range(0, 20, 5):
print(i) # 0, 5, 10, 15
# Parcourir une chaîne
for lettre in "Python":
print(lettre) # P, y, t, h, o, n
# Parcourir une liste
fruits = ["pomme", "banane", "cerise"]
for fruit in fruits:
print(fruit)
2.3 Boucle while
La boucle while tourne tant qu'une condition est vraie :
python
compteur = 0
while compteur < 5:
print(compteur)
compteur += 1 # IMPORTANT : ne pas oublier sinon boucle infinie !
Contrôle de boucle :
python
# break → quitter la boucle immédiatement
for i in range(10):
if i == 5:
break
print(i) # affiche 0, 1, 2, 3, 4
# continue → passer à l'itération suivante
for i in range(6):
if i == 3:
continue
print(i) # affiche 0, 1, 2, 4, 5
# pass → ne rien faire (placeholder)
for i in range(5):
pass # boucle vide, ne plante pas
Exercice 2 — FizzBuzz (classique incontournable)
Affiche les nombres de 1 à 50. Mais :
• Si divisible par 3 → affiche Fizz
• Si divisible par 5 → affiche Buzz
• Si divisible par 3 ET 5 → affiche FizzBuzz
• Sinon → affiche le nombre
Astuce : teste d'abord % 15 (multiple de 3 ET 5) avant les autres cas.
Solution :
python
for i in range(1, 51):
if i % 15 == 0:
print("FizzBuzz")
elif i % 3 == 0:
print("Fizz")
elif i % 5 == 0:
print("Buzz")
else:
print(i)
Exercice 3 — Devine le nombre
Génère un nombre aléatoire entre 1 et 100. Demande à l'utilisateur de le deviner. À chaque
essai, indique si c'est trop grand, trop petit ou trouvé ! Compte le nombre d'essais.
Solution :
python
import random
secret = [Link](1, 100)
essais = 0
trouve = False
while not trouve:
guess = int(input("Ton nombre : "))
essais += 1
if guess < secret:
print("Trop petit !")
elif guess > secret:
print("Trop grand !")
else:
print(f"Bravo ! Trouvé en {essais} essais.")
trouve = True
Chapitre 3 — Fonctions
3.1 Définir et appeler une fonction
Une fonction est un bloc de code réutilisable qu'on nomme.
python
def saluer(prenom):
"""Cette ligne s'appelle une docstring, elle documente la fonction."""
message = f"Bonjour, {prenom} !"
return message
resultat = saluer("Mathis")
print(resultat) # Bonjour, Mathis !
return renvoie une valeur. Sans return, la fonction renvoie None.
Une fonction peut retourner plusieurs valeurs (sous forme de tuple) :
python
def stats(liste):
return min(liste), max(liste), sum(liste) / len(liste)
mini, maxi, moy = stats([10, 5, 8, 20, 3])
print(mini, maxi, moy) # 3 20 9.2
3.2 Arguments par défaut
python
def puissance(base, exposant=2): # exposant vaut 2 par défaut
return base ** exposant
print(puissance(5)) # 25 (exposant = 2 par défaut)
print(puissance(5, 3)) # 125
print(puissance(exposant=4, base=2)) # 16 → arguments nommés
3.3 *args et **kwargs
python
# *args → reçoit autant d'arguments positionnels qu'on veut
def total(*nombres):
return sum(nombres)
print(total(1, 2, 3)) #6
print(total(10, 20, 30, 40)) # 100
# **kwargs → reçoit autant d'arguments nommés qu'on veut
def afficher_profil(**infos):
for cle, valeur in [Link]():
print(f" {cle} : {valeur}")
afficher_profil(nom="Mathis", age=20, ville="Lyon")
# nom : Mathis
# age : 20
# ville : Lyon
3.4 Fonctions lambda
Une lambda est une fonction anonyme en une seule ligne :
python
double = lambda x: x * 2
print(double(7)) # 14
additionner = lambda a, b: a + b
print(additionner(3, 4)) # 7
# Très utiles avec sorted(), map(), filter()
noms = ["Charlie", "Alice", "Bob", "Diana"]
[Link](key=lambda n: len(n)) # trier par longueur
print(noms) # ['Bob', 'Alice', 'Diana', 'Charlie']
Exercice 4 — Calculatrice
Écris une fonction calculer(a, operateur, b) qui supporte +, -, *, /, ** et %.
• Retourne le résultat.
• Affiche une erreur propre si division par zéro.
• Retourne None si l'opérateur est inconnu.
Solution :
python
def calculer(a, operateur, b):
if operateur == "+":
return a + b
elif operateur == "-":
return a - b
elif operateur == "*":
return a * b
elif operateur == "/":
if b == 0:
print("Erreur : division par zéro impossible.")
return None
return a / b
elif operateur == "**":
return a ** b
elif operateur == "%":
return a % b
else:
print(f"Opérateur '{operateur}' inconnu.")
return None
print(calculer(10, "/", 0)) # Erreur : division par zéro impossible.
print(calculer(7, "*", 6)) # 42
print(calculer(2, "**", 10)) # 1024
Exercice 5 — Suite de Fibonacci
Écris une fonction fibonacci(n) qui retourne la liste des n premiers termes de la suite de
Fibonacci. La suite commence par [0, 1] et chaque terme suivant est la somme des deux
précédents : 0, 1, 1, 2, 3, 5, 8, 13, ...
Solution :
python
def fibonacci(n):
if n <= 0:
return []
if n == 1:
return [0]
suite = [0, 1]
while len(suite) < n:
[Link](suite[-1] + suite[-2])
return suite
print(fibonacci(10))
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
Chapitre 4 — Structures de données
4.1 Listes
La liste est la structure la plus utilisée. Elle est ordonnée et mutable (modifiable).
python
fruits = ["pomme", "banane", "cerise", "mangue"]
# Accès par index (commence à 0)
print(fruits[0]) # pomme
print(fruits[-1]) # mangue (dernier élément)
print(fruits[-2]) # cerise (avant-dernier)
# Slicing [début:fin:pas] — fin exclue
print(fruits[1:3]) # ['banane', 'cerise']
print(fruits[:2]) # ['pomme', 'banane']
print(fruits[2:]) # ['cerise', 'mangue']
print(fruits[::2]) # ['pomme', 'cerise'] (un sur deux)
print(fruits[::-1]) # liste inversée
Méthodes essentielles :
python
[Link]("kiwi") # ajoute à la fin
[Link](1, "fraise") # insère à l'index 1
[Link]("banane") # supprime par valeur (1ère occurrence)
[Link]() # supprime et retourne le dernier
[Link](0) # supprime et retourne l'index 0
[Link]() # trie en place
[Link](reverse=True) # trie en ordre décroissant
[Link]() # inverse en place
[Link]("pomme") # nombre d'occurrences
[Link]("cerise") # position de "cerise"
len(fruits) # longueur
"pomme" in fruits # True si présent
4.2 Tuples
Immuables (non modifiables). Parfaits pour des données fixes ou pour retourner plusieurs
valeurs.
python
point = (3, 7)
couleur = (255, 128, 0) # RGB orange
coordonnees = (48.8566, 2.3522)
# Unpacking
x, y = point
print(x, y) # 3 7
r, g, b = couleur
lat, lon = coordonnees
# Un tuple d'un seul élément (noter la virgule !)
singleton = (42,)
4.3 Dictionnaires
Paires clé : valeur, ordonnés (Python 3.7+), mutables.
python
joueur = {
"nom": "Mathis",
"niveau": 12,
"vies": 3,
"score": 8450
}
# Accès
print(joueur["nom"]) # Mathis
print([Link]("mana", 100)) # 100 (valeur par défaut si absent)
# Modification / ajout
joueur["niveau"] = 13
joueur["xp"] = 2500
# Suppression
del joueur["vies"]
[Link]("xp") # supprime et retourne la valeur
# Parcourir
for cle in joueur:
print(cle)
for cle, val in [Link]():
print(f"{cle} → {val}")
# Vérifier l'existence
if "score" in joueur:
print("Score trouvé !")
4.4 Sets (ensembles)
Non ordonnés, sans doublons. Très rapides pour tester l'appartenance.
python
couleurs = {"rouge", "bleu", "vert"}
[Link]("jaune")
[Link]("rouge") # ne plante pas si absent
# Opérations ensemblistes
a = {1, 2, 3, 4, 5}
b = {3, 4, 5, 6, 7}
print(a & b) # {3, 4, 5} → intersection
print(a | b) # {1,2,3,4,5,6,7} → union
print(a - b) # {1, 2} → différence
print(a ^ b) # {1, 2, 6, 7} → différence symétrique
# Supprimer les doublons d'une liste
avec_doublons = [1, 2, 2, 3, 3, 3, 4]
sans_doublons = list(set(avec_doublons))
Exercice 6 — Gestion d'inventaire
Crée un dictionnaire inventaire = {"épée": 1, "potion": 5, "flèche": 30}.
Écris trois fonctions :
• ajouter(inv, objet, qte) — ajoute la quantité (crée l'objet s'il n'existe pas)
• retirer(inv, objet, qte) — retire la quantité, affiche une erreur si stock insuffisant,
supprime l'entrée si quantité = 0
• afficher(inv) — affiche chaque objet aligné avec sa quantité
Solution :
python
inventaire = {"épée": 1, "potion": 5, "flèche": 30}
def ajouter(inv, objet, qte):
inv[objet] = [Link](objet, 0) + qte
print(f"+{qte} {objet} ajouté(s). Total : {inv[objet]}")
def retirer(inv, objet, qte):
if [Link](objet, 0) < qte:
print(f"Erreur : stock insuffisant pour '{objet}'.")
return
inv[objet] -= qte
if inv[objet] == 0:
del inv[objet]
print(f"'{objet}' épuisé, retiré de l'inventaire.")
else:
print(f"-{qte} {objet}. Restant : {inv[objet]}")
def afficher(inv):
print("─── Inventaire ───")
for objet, qte in sorted([Link]()):
print(f" {objet:<15} x{qte}")
ajouter(inventaire, "bouclier", 1)
retirer(inventaire, "potion", 5) # épuise les potions
afficher(inventaire)
Chapitre 5 — Chaînes de caractères
5.1 Méthodes essentielles
python
texte = " Bonjour, Python ! "
# Nettoyage
[Link]() # "Bonjour, Python !" — supprime les espaces en bord
[Link]() # supprime à gauche seulement
[Link]() # supprime à droite seulement
# Casse
[Link]() # "bonjour, python !"
[Link]() # "BONJOUR, PYTHON !"
[Link]() # "Bonjour, Python !" — 1ère lettre en maj
[Link]() # " bonjour, python !" — seulement la 1ère
# Recherche & remplacement
[Link]("Python", "Monde") # " Bonjour, Monde ! "
[Link]("Python") # index de début, -1 si absent
[Link]("o") # nombre d'occurrences
"Python" in texte # True
# Découpage / assemblage
"un,deux,trois".split(",") # ['un', 'deux', 'trois']
"hello".split() # ['hello'] (espace par défaut)
" - ".join(["un", "deux", "trois"]) # "un - deux - trois"
# Vérifications (retournent True/False)
"123".isdigit() # True
"abc".isalpha() # True
"abc123".isalnum() # True
" ".isspace() # True
"Hello".startswith("He") # True
"Hello".endswith("lo") # True
5.2 F-strings avancées
python
nom = "Mathis"
score = 1234.5678
pi = 3.14159
# Décimales
print(f"{score:.2f}") # 1234.57
print(f"{pi:.4f}") # 3.1416
# Alignement et largeur
print(f"{nom:>15}") #" Mathis" (aligné à droite, 15 chars)
print(f"{nom:<15}") # "Mathis " (aligné à gauche)
print(f"{nom:^15}") # " Mathis " (centré)
print(f"{nom:*^15}") # "****Mathis*****" (centré avec remplissage *)
# Entiers
print(f"{1000000:,}") # 1,000,000 (séparateur milliers)
print(f"{255:08b}") # 11111111 (binaire sur 8 bits)
print(f"{255:02x}") # ff (hexadécimal)
# Calculs inline
print(f"2 ** 10 = {2**10}") # 2 ** 10 = 1024
Exercice 7 — Analyseur de texte
Écris une fonction analyser(texte) qui affiche :
• Le nombre de mots
• Le nombre de phrases (se termine par ., ! ou ?)
• Le mot le plus fréquent
• Le mot le plus long
• Le texte avec chaque mot commençant par une majuscule
Solution :
python
def analyser(texte):
mots = [Link]().split()
phrases = [Link](".") + [Link]("!") + [Link]("?")
# Fréquence des mots (sans ponctuation)
freq = {}
for mot in mots:
mot_propre = [Link](".,!?;:")
freq[mot_propre] = [Link](mot_propre, 0) + 1
mot_frequent = max(freq, key=[Link])
mot_long = max(mots, key=len).strip(".,!?;:")
print(f"Mots : {len(mots)}")
print(f"Phrases : {phrases}")
print(f"Mot fréquent : '{mot_frequent}' ({freq[mot_frequent]}x)")
print(f"Mot le + long : '{mot_long}'")
print(f"En titre : {[Link]()}")
analyser("Python est génial. Python est simple et puissant ! Tu aimes Python ?")
Chapitre 6 — Modules et fichiers
6.1 Modules standard incontournables
python
import math
[Link](16) # 4.0 — racine carrée
[Link](3.2) # 4 — arrondi supérieur
[Link](3.9) # 3 — arrondi inférieur
[Link] # 3.14159...
[Link](100, 10) # 2.0 — logarithme base 10
import random
[Link](1, 6) # entier entre 1 et 6 inclus
[Link]() # float entre 0.0 et 1.0
[Link](["A", "B", "C"]) # élément aléatoire
[Link](liste) # mélange en place
[Link](liste, 3) # 3 éléments sans répétition
from datetime import datetime, timedelta
now = [Link]()
print([Link]("%d/%m/%Y %H:%M")) # 24/03/2026 09:30
hier = now - timedelta(days=1)
import os
[Link]() # dossier courant
[Link](".") # contenu du dossier
[Link]("fich") # True si le fichier existe
[Link]("dossier") # crée un dossier (et ses parents)
6.2 Lecture et écriture de fichiers
python
# Écriture (crée ou écrase le fichier)
with open("[Link]", "w", encoding="utf-8") as f:
[Link]("Ligne 1\n")
[Link]("Ligne 2\n")
# Ajout à la fin (mode "a")
with open("[Link]", "a", encoding="utf-8") as f:
[Link]("Ligne 3\n")
# Lecture complète
with open("[Link]", "r", encoding="utf-8") as f:
contenu = [Link]()
print(contenu)
# Lecture ligne par ligne (économe en mémoire)
with open("[Link]", "r", encoding="utf-8") as f:
for ligne in f:
print([Link]())
# Lecture de toutes les lignes dans une liste
with open("[Link]", "r", encoding="utf-8") as f:
lignes = [Link]() # ['"Ligne 1\n", "Ligne 2\n", ...]
Toujours utiliser with open(...) — il ferme automatiquement le fichier même en cas
d'erreur.
Chapitre 7 — Gestion des erreurs
7.1 try / except / finally
python
try:
x = int(input("Entrez un nombre : "))
resultat = 100 / x
print(f"100 / {x} = {resultat}")
except ValueError:
print("Erreur : veuillez entrer un nombre entier.")
except ZeroDivisionError:
print("Erreur : impossible de diviser par zéro.")
except Exception as e:
print(f"Erreur inattendue : {e}")
else:
print("Tout s'est bien passé !") # exécuté si PAS d'erreur
finally:
print("Ce bloc s'exécute TOUJOURS.")
Erreurs courantes à connaître :
Exception Cause typique
ValueError int("abc") — mauvais type de valeur
TypeError "2" + 2 — types incompatibles
Exception Cause typique
IndexError liste[99] — index hors limites
KeyError dico["clé"] — clé absente
ZeroDivisionError x / 0
FileNotFoundError fichier inexistant
AttributeError attribut inexistant sur un objet
7.2 Lever ses propres exceptions
python
def verifier_age(age):
if not isinstance(age, int):
raise TypeError("L'âge doit être un entier.")
if age < 0 or age > 150:
raise ValueError(f"Âge invalide : {age}. Doit être entre 0 et 150.")
return age
try:
verifier_age(-5)
except ValueError as e:
print(e) # Âge invalide : -5. Doit être entre 0 et 150.
Exercice 8 — Saisie robuste
Écris une fonction demander_entier(message, mini, maxi) qui :
• Demande en boucle jusqu'à obtenir un entier valide
• Vérifie que la valeur est entre mini et maxi
• Affiche un message d'erreur clair à chaque tentative invalide
• Retourne la valeur valide
Solution :
python
def demander_entier(message, mini, maxi):
while True:
try:
valeur = int(input(message))
if valeur < mini or valeur > maxi:
print(f"Erreur : entrez un nombre entre {mini} et {maxi}.")
else:
return valeur
except ValueError:
print("Erreur : ce n'est pas un nombre entier.")
age = demander_entier("Votre âge : ", 0, 150)
print(f"Âge enregistré : {age}")
Chapitre 8 — Programmation Orientée Objet
8.1 Classe et objet
Une classe est un modèle (plan). Un objet est une instance de ce modèle.
python
class Personnage:
"""Un personnage de jeu vidéo."""
def __init__(self, nom, vie=100, force=10):
# self = l'objet lui-même
[Link] = nom
[Link] = vie
[Link] = force
[Link] = 1
[Link] = True
def attaquer(self, cible):
if not [Link]:
print(f"{[Link]} est mort et ne peut pas attaquer.")
return
degats = [Link]
cible.recevoir_degats(degats)
print(f"{[Link]} attaque {[Link]} pour {degats} dégâts !")
def recevoir_degats(self, degats):
[Link] -= degats
if [Link] <= 0:
[Link] = 0
[Link] = False
print(f"{[Link]} est mort !")
def soigner(self, soin):
[Link] = min([Link] + soin, 100) # max 100
print(f"{[Link]} récupère {soin} PV. Vie : {[Link]}")
def __str__(self):
statut = "vivant" if [Link] else "mort"
return f"{[Link]} | Niv.{[Link]} | {[Link]} | {statut}"
# Créer des objets
hero = Personnage("Arthas", vie=150, force=20)
gobelin = Personnage("Goblin", vie=40, force=8)
[Link](gobelin) # Arthas attaque Goblin pour 20 dégâts !
print(gobelin) # Goblin | Niv.1 | 20 | vivant
[Link](gobelin) # Goblin est mort !
8.2 Attributs de classe vs d'instance
python
class Compteur:
instances = 0 # attribut de CLASSE (partagé par tous)
def __init__(self, nom):
[Link] = nom # attribut d'INSTANCE
[Link] += 1
@classmethod
def combien(cls):
return [Link]
@staticmethod
def aide():
return "Je compte les instances créées."
c1 = Compteur("A")
c2 = Compteur("B")
print([Link]()) # 2
print([Link]())
8.3 Héritage
python
class Magicien(Personnage): # hérite de Personnage
def __init__(self, nom):
super().__init__(nom, vie=80, force=5) # appel du parent
[Link] = 100
[Link] = []
def apprendre_sort(self, sort):
[Link](sort)
print(f"{[Link]} apprend '{sort}' !")
def lancer_sort(self, cible, sort, degats):
if sort not in [Link]:
print(f"{[Link]} ne connaît pas '{sort}'.")
return
if [Link] < 20:
print("Mana insuffisant !")
return
cible.recevoir_degats(degats * 2) # sorts = double dégâts
[Link] -= 20
print(f"{[Link]} lance '{sort}' → {degats*2} dégâts magiques !")
def __str__(self):
base = super().__str__()
return f"{base} | {[Link]} mana"
mage = Magicien("Gandalf")
mage.apprendre_sort("Boule de feu")
mage.lancer_sort(gobelin, "Boule de feu", 15)
print(mage)
8.4 Méthodes spéciales (dunder methods)
python
class Vecteur:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self): # print(v) → affichage lisible
return f"Vecteur({self.x}, {self.y})"
def __repr__(self): # représentation technique
return f"Vecteur(x={self.x}, y={self.y})"
def __add__(self, autre): # v1 + v2
return Vecteur(self.x + autre.x, self.y + autre.y)
def __len__(self): # len(v)
return int((self.x**2 + self.y**2) ** 0.5)
def __eq__(self, autre): # v1 == v2
return self.x == autre.x and self.y == autre.y
v1 = Vecteur(3, 4)
v2 = Vecteur(1, 2)
print(v1 + v2) # Vecteur(4, 6)
print(len(v1)) # 5 (norme entière)
print(v1 == v2) # False
Exercice 9 — Banque orientée objet
Crée une classe CompteBancaire :
• Attributs : titulaire, solde (défaut 0), historique (liste des transactions)
• Méthodes : deposer(montant), retirer(montant), afficher_historique()
• Gère les erreurs (montant négatif, solde insuffisant)
Puis crée CompteEpargne(CompteBancaire) avec une méthode appliquer_interets(taux_pct).
Solution :
python
class CompteBancaire:
def __init__(self, titulaire, solde=0):
[Link] = titulaire
[Link] = solde
[Link] = []
def deposer(self, montant):
if montant <= 0:
raise ValueError("Le montant doit être positif.")
[Link] += montant
[Link](f"+{montant}€ (dépôt)")
print(f"Dépôt de {montant}€. Solde : {[Link]}€")
def retirer(self, montant):
if montant <= 0:
raise ValueError("Le montant doit être positif.")
if montant > [Link]:
raise ValueError("Solde insuffisant.")
[Link] -= montant
[Link](f"-{montant}€ (retrait)")
print(f"Retrait de {montant}€. Solde : {[Link]}€")
def afficher_historique(self):
print(f"\n── Historique de {[Link]} ──")
for ligne in [Link]:
print(f" {ligne}")
print(f" Solde actuel : {[Link]}€\n")
def __str__(self):
return f"Compte de {[Link]} : {[Link]}€"
class CompteEpargne(CompteBancaire):
def appliquer_interets(self, taux_pct):
interets = [Link] * taux_pct / 100
[Link] += interets
[Link](f"+{interets:.2f}€ (intérêts {taux_pct}%)")
print(f"Intérêts de {taux_pct}% : +{interets:.2f}€. Solde : {[Link]:.2f}€")
# Test
compte = CompteEpargne("Mathis", 1000)
[Link](500)
[Link](200)
compte.appliquer_interets(3)
compte.afficher_historique()
Chapitre 9 — Notions intermédiaires
9.1 Compréhensions
python
# List comprehension
carres = [x**2 for x in range(10)]
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
pairs = [x for x in range(20) if x % 2 == 0]
# [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
# Imbriqué — produit cartésien
paires = [(x, y) for x in range(3) for y in range(3)]
# [(0,0), (0,1), (0,2), (1,0), ...]
# Dict comprehension
mots = ["chat", "chien", "oiseau"]
longueurs = {mot: len(mot) for mot in mots}
# {'chat': 4, 'chien': 5, 'oiseau': 6}
# Set comprehension
lettres = {lettre for lettre in "bonjour"}
# {'b', 'o', 'n', 'j', 'u', 'r'} — sans doublons
9.2 enumerate, zip, sorted
python
# enumerate → donne index + valeur
fruits = ["pomme", "banane", "cerise"]
for i, fruit in enumerate(fruits, start=1):
print(f"{i}. {fruit}")
# 1. pomme 2. banane 3. cerise
# zip → parcourir plusieurs listes ensemble
noms = ["Alice", "Bob", "Charlie"]
scores = [95, 80, 72]
for nom, score in zip(noms, scores):
print(f"{nom} : {score}/100")
# sorted → retourne une NOUVELLE liste triée
nombres = [3, 1, 4, 1, 5, 9, 2, 6]
tries = sorted(nombres) # croissant
desc = sorted(nombres, reverse=True) # décroissant
joueurs = [{"nom": "Alice", "pts": 95}, {"nom": "Bob", "pts": 80}]
classe = sorted(joueurs, key=lambda j: j["pts"], reverse=True)
9.3 map et filter
python
nombres = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# map → applique une fonction à chaque élément
doubles = list(map(lambda x: x * 2, nombres))
# [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
# filter → garde les éléments qui satisfont une condition
pairs = list(filter(lambda x: x % 2 == 0, nombres))
# [2, 4, 6, 8, 10]
# reduce → accumule (nécessite functools)
from functools import reduce
produit = reduce(lambda acc, x: acc * x, [1, 2, 3, 4, 5])
# 120 (= 1 × 2 × 3 × 4 × 5)
Exercice 10 — Projet final : Gestionnaire de notes
Crée un programme complet qui :
1. Stocke des étudiants et leurs notes.
2. Calcule la moyenne de chaque étudiant.
3. Attribue une mention selon la moyenne.
4. Trie par moyenne décroissante.
5. Affiche le classement formaté.
6. Sauvegarde dans [Link].
Solution :
python
etudiants = {
"Alice": [15, 17, 14, 16, 18],
"Bob": [10, 12, 9, 11, 8],
"Charlie": [18, 19, 17, 20, 16],
"Diana": [ 8, 7, 9, 6, 10],
"Evan": [13, 12, 14, 11, 15],
def mention(moy):
if moy >= 16: return "Très bien "
if moy >= 14: return "Bien "
if moy >= 10: return "Passable "
return "Insuffisant "
# Calcul des moyennes
resultats = [
"nom": nom,
"moy": sum(notes) / len(notes),
"mention": mention(sum(notes) / len(notes))
for nom, notes in [Link]()
# Tri par moyenne décroissante
[Link](key=lambda e: e["moy"], reverse=True)
# Affichage + sauvegarde
lignes = ["=== Classement des étudiants ===\n"]
for rang, e in enumerate(resultats, 1):
ligne = f"{rang}. {e['nom']:<10} {e['moy']:5.2f}/20 {e['mention']}"
print(ligne)
[Link](ligne + "\n")
with open("[Link]", "w", encoding="utf-8") as f:
[Link](lignes)
print("\nClassement sauvegardé dans [Link] ")
Récapitulatif — Ce que tu maîtrises maintenant
Notion Chapitres
Variables, types, opérateurs 1
Conditions et boucles 2
Fonctions, lambda, *args 3
Listes, tuples, dicts, sets 4
Chaînes et f-strings 5
Modules standard et fichiers 6
Gestion des erreurs 7
Classes, héritage, dunder 8
Compréhensions, zip, map 9
Prochaines étapes recommandées :
• Décorateurs (@property, @staticmethod, créer les siens)
• Générateurs (yield)
• Modules json, csv, re (regex)
• Bibliothèques : requests, pandas, pygame selon tes projets
Peut tu ajouter un cours sur le module os, json, tkinter, threading
07:31
Chapitre 10 — Le module os
Le module os permet d'interagir avec le système de fichiers et le système d'exploitation.
10.1 Navigation et informations
python
import os
# Dossier courant
print([Link]()) # /home/mathis/projets
# Changer de dossier
[Link]("/home/mathis/Documents")
# Lister le contenu d'un dossier
contenu = [Link](".") # liste tous les fichiers/dossiers
for item in contenu:
print(item)
# Informations sur un chemin
[Link]("[Link]") # True si existe
[Link]("[Link]") # True si c'est un fichier
[Link]("dossier") # True si c'est un dossier
[Link]("[Link]") # taille en octets
[Link]("/a/b/[Link]") # "[Link]"
[Link]("/a/b/[Link]") # "/a/b"
[Link]("[Link]") # ("fichier", ".txt")
# Construire un chemin (compatible Windows/Linux/Mac)
chemin = [Link]("dossier", "sous-dossier", "[Link]")
print(chemin) # dossier/sous-dossier/[Link] (ou \ sur Windows)
10.2 Créer, renommer, supprimer
python
import os
# Créer des dossiers
[Link]("nouveau_dossier") # crée un dossier (erreur si existe déjà)
[Link]("a/b/c", exist_ok=True) # crée tous les dossiers intermédiaires
# Renommer / déplacer
[Link]("ancien_nom.txt", "nouveau_nom.txt")
# Supprimer
[Link]("[Link]") # supprime un fichier
[Link]("dossier_vide") # supprime un dossier VIDE seulement
# Pour supprimer un dossier non vide :
import shutil
[Link]("dossier_avec_contenu") # irrécupérable !
[Link]("[Link]", "[Link]") # copier un fichier
[Link]("[Link]", "destination/") # déplacer
10.3 Parcourir une arborescence avec [Link]
[Link] est extrêmement utile pour explorer récursivement tous les fichiers d'un dossier.
python
import os
for dossier, sous_dossiers, fichiers in [Link]("."):
print(f"\n {dossier}")
for fichier in fichiers:
chemin_complet = [Link](dossier, fichier)
taille = [Link](chemin_complet)
print(f" {fichier} ({taille} octets)")
10.4 Variables d'environnement
python
import os
# Lire une variable d'environnement
path = [Link]("PATH", "non défini")
maison = [Link]("HOME")
# Définir une variable (pour ce processus uniquement)
[Link]["MON_APP_MODE"] = "debug"
# Exemple concret : clé API stockée en variable d'environnement
api_key = [Link]("API_KEY")
if not api_key:
raise EnvironmentError("La variable API_KEY n'est pas définie !")
Exercice 11 — Explorateur de fichiers
Écris une fonction scanner_dossier(chemin) qui :
1. Parcourt récursivement le dossier donné.
2. Compte le nombre de fichiers par extension (.py, .txt, .jpg...).
3. Calcule la taille totale de chaque type.
4. Affiche un rapport trié par nombre de fichiers décroissant.
Solution :
python
import os
def scanner_dossier(chemin):
stats = {} # {"ext": {"nb": 0, "taille": 0}}
for dossier, _, fichiers in [Link](chemin):
for fichier in fichiers:
_, ext = [Link](fichier)
ext = [Link]() if ext else "(sans extension)"
chemin_f = [Link](dossier, fichier)
if ext not in stats:
stats[ext] = {"nb": 0, "taille": 0}
stats[ext]["nb"] += 1
stats[ext]["taille"] += [Link](chemin_f)
print(f"\n{'Extension':<20} {'Fichiers':>10} {'Taille':>15}")
print("─" * 47)
for ext, info in sorted([Link](), key=lambda x: x[1]["nb"], reverse=True):
taille_ko = info["taille"] / 1024
print(f"{ext:<20} {info['nb']:>10} {taille_ko:>13.1f} Ko")
scanner_dossier(".")
Chapitre 11 — Le module json
JSON (JavaScript Object Notation) est le format d'échange de données le plus utilisé sur le
web. Python le gère nativement.
11.1 Les équivalences Python ↔ JSON
Python JSON
dict object {}
list array []
str string ""
int, float number
True / False true / false
None null
11.2 Sérialisation — Python → JSON
python
import json
joueur = {
"nom": "Mathis",
"niveau": 12,
"vies": 3,
"actif": True,
"scores": [1200, 850, 2100],
"position": {"x": 14.5, "y": 32.0}
# Convertir en chaîne JSON
texte_json = [Link](joueur)
print(texte_json)
# {"nom": "Mathis", "niveau": 12, ...}
# Avec indentation (lisible)
texte_joli = [Link](joueur, indent=4, ensure_ascii=False)
print(texte_joli)
# Sauvegarder dans un fichier
with open("[Link]", "w", encoding="utf-8") as f:
[Link](joueur, f, indent=4, ensure_ascii=False)
ensure_ascii=False est indispensable pour les caractères accentués (é, à, ç...).
11.3 Désérialisation — JSON → Python
python
import json
# Depuis une chaîne
texte = '{"nom": "Alice", "score": 95, "actif": true}'
data = [Link](texte)
print(data["nom"]) # Alice
print(type(data)) # <class 'dict'>
# Depuis un fichier
with open("[Link]", "r", encoding="utf-8") as f:
joueur = [Link](f)
print(joueur["nom"]) # Mathis
print(joueur["scores"][0]) # 1200
11.4 Cas pratique — Fichier de configuration
python
import json, os
FICHIER_CONFIG = "[Link]"
CONFIG_DEFAUT = {
"langue": "fr",
"theme": "sombre",
"volume": 80,
"plein_ecran": False
def charger_config():
if [Link](FICHIER_CONFIG):
with open(FICHIER_CONFIG, "r", encoding="utf-8") as f:
return [Link](f)
return CONFIG_DEFAUT.copy()
def sauvegarder_config(config):
with open(FICHIER_CONFIG, "w", encoding="utf-8") as f:
[Link](config, f, indent=4, ensure_ascii=False)
print("Configuration sauvegardée ")
# Utilisation
config = charger_config()
config["volume"] = 65
config["theme"] = "clair"
sauvegarder_config(config)
Exercice 12 — Carnet de contacts JSON
Crée un carnet de contacts persistant (sauvegardé en JSON) avec :
• ajouter_contact(nom, tel, email)
• rechercher_contact(nom) — recherche partielle insensible à la casse
• supprimer_contact(nom)
• lister_contacts()
Les données doivent survivre entre deux exécutions du programme.
Solution :
python
import json, os
FICHIER = "[Link]"
def charger():
if [Link](FICHIER):
with open(FICHIER, "r", encoding="utf-8") as f:
return [Link](f)
return []
def sauvegarder(contacts):
with open(FICHIER, "w", encoding="utf-8") as f:
[Link](contacts, f, indent=4, ensure_ascii=False)
def ajouter_contact(nom, tel, email):
contacts = charger()
[Link]({"nom": nom, "tel": tel, "email": email})
sauvegarder(contacts)
print(f"Contact '{nom}' ajouté ")
def rechercher_contact(terme):
contacts = charger()
resultats = [c for c in contacts if [Link]() in c["nom"].lower()]
if not resultats:
print("Aucun contact trouvé.")
for c in resultats:
print(f" {c['nom']} | {c['tel']} | {c['email']}")
def supprimer_contact(nom):
contacts = charger()
avant = len(contacts)
contacts = [c for c in contacts if c["nom"].lower() != [Link]()]
if len(contacts) == avant:
print(f"Contact '{nom}' introuvable.")
else:
sauvegarder(contacts)
print(f"Contact '{nom}' supprimé ")
def lister_contacts():
contacts = charger()
if not contacts:
print("Carnet vide.")
return
print(f"\n{'Nom':<20} {'Téléphone':<15} {'Email'}")
print("─" * 55)
for c in sorted(contacts, key=lambda x: x["nom"]):
print(f"{c['nom']:<20} {c['tel']:<15} {c['email']}")
# Test
ajouter_contact("Mathis", "06 12 34 56 78", "mathis@[Link]")
ajouter_contact("Alice", "07 98 76 54 32", "alice@[Link]")
lister_contacts()
rechercher_contact("mat")
supprimer_contact("Alice")
Chapitre 12 — tkinter — Interfaces graphiques
tkinter est la bibliothèque GUI incluse avec Python. Elle permet de créer des fenêtres,
boutons, champs de texte, etc.
12.1 Fenêtre de base et widgets essentiels
python
import tkinter as tk
# Créer la fenêtre principale
fenetre = [Link]()
[Link]("Ma première fenêtre")
[Link]("400x300") # largeur x hauteur
[Link](False, False) # non redimensionnable
# Label — texte statique
label = [Link](fenetre, text="Bonjour !", font=("Arial", 16), fg="blue")
[Link](pady=20)
# Button — bouton cliquable
def au_clic():
[Link](text="Bouton cliqué !")
bouton = [Link](fenetre, text="Clique-moi", command=au_clic,
bg="#2E75B6", fg="white", font=("Arial", 12))
[Link](pady=10)
# Entry — champ de saisie
champ = [Link](fenetre, font=("Arial", 12), width=25)
[Link](pady=10)
def afficher_saisie():
texte = [Link]()
[Link](text=f"Tu as écrit : {texte}")
[Link](fenetre, text="Afficher", command=afficher_saisie).pack()
# Lancer la boucle d'événements (TOUJOURS en dernier)
[Link]()
12.2 Les gestionnaires de disposition
python
import tkinter as tk
fenetre = [Link]()
[Link]("400x250")
# ── pack ── (simple, empile les widgets)
[Link](fenetre, text="pack → haut").pack(side="top", pady=5)
[Link](fenetre, text="pack → bas").pack(side="bottom", pady=5)
[Link](fenetre, text="pack → gauche").pack(side="left", padx=5)
[Link](fenetre, text="pack → droite").pack(side="right", padx=5)
# ── grid ── (grille ligne/colonne, le plus flexible)
frame = [Link](fenetre)
[Link]()
[Link](frame, text="Nom :").grid(row=0, column=0, sticky="e", padx=5, pady=5)
[Link](frame).grid(row=0, column=1, padx=5)
[Link](frame, text="Email :").grid(row=1, column=0, sticky="e", padx=5, pady=5)
[Link](frame).grid(row=1, column=1, padx=5)
[Link](frame, text="Valider").grid(row=2, column=0, columnspan=2, pady=10)
[Link]()
12.3 Variables tkinter et widgets avancés
python
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
fenetre = [Link]()
[Link]("Widgets avancés")
[Link]("450x400")
# Variables tkinter (liées automatiquement aux widgets)
nom_var = [Link](value="Mathis")
age_var = [Link](value=20)
accord = [Link](value=False)
choix = [Link](value="Python")
# Entry lié à une StringVar
[Link](fenetre, text="Nom :").pack()
[Link](fenetre, textvariable=nom_var).pack()
# Scale (curseur)
[Link](fenetre, text="Âge :").pack()
[Link](fenetre, variable=age_var, from_=0, to=100,
orient="horizontal", length=300).pack()
# Checkbutton
[Link](fenetre, text="J'accepte les CGU",
variable=accord).pack(pady=5)
# Combobox (liste déroulante)
[Link](fenetre, text="Langage préféré :").pack()
combo = [Link](fenetre, textvariable=choix,
values=["Python", "JavaScript", "C++", "Rust"])
[Link]()
def valider():
if not [Link]():
[Link]("Attention", "Accepte les CGU d'abord !")
return
[Link]("Résultat",
f"Nom : {nom_var.get()}\nÂge : {age_var.get()}\nLangage : {[Link]()}")
[Link](fenetre, text="Valider", command=valider,
bg="green", fg="white").pack(pady=15)
# Boîtes de dialogue
def ouvrir_fichier():
fichier = [Link](
title="Choisir un fichier",
filetypes=[("Fichiers texte", "*.txt"), ("Tous", "*.*")]
if fichier:
print(f"Fichier choisi : {fichier}")
[Link](fenetre, text="Ouvrir fichier...", command=ouvrir_fichier).pack()
[Link]()
Exercice 13 — Mini calculatrice graphique
Crée une calculatrice avec tkinter qui :
• Affiche un écran (Label ou Entry en lecture seule)
• A des boutons 0–9, +, −, ×, ÷, =, C
• Gère les erreurs (division par zéro)
• A un design soigné avec des couleurs différentes pour les opérateurs
Solution :
python
import tkinter as tk
fenetre = [Link]()
[Link]("Calculatrice")
[Link]("320x420")
[Link](False, False)
[Link](bg="#1e1e2e")
expression = [Link](value="")
# Écran
ecran = [Link](
fenetre, textvariable=expression, font=("Arial", 22),
justify="right", bd=0, bg="#313244", fg="white",
insertbackground="white", relief="flat"
[Link](fill="x", padx=15, pady=(20, 10), ipady=12)
def appuyer(valeur):
[Link]([Link]() + str(valeur))
def effacer():
[Link]("")
def calculer():
try:
expr = [Link]().replace("×", "*").replace("÷", "/")
resultat = eval(expr)
# Éviter l'affichage de .0 inutile
if isinstance(resultat, float) and resultat.is_integer():
resultat = int(resultat)
[Link](resultat)
except ZeroDivisionError:
[Link]("Erreur : div/0")
except Exception:
[Link]("Erreur")
# Disposition des boutons
boutons = [
["C", "←", "%", "÷"],
["7", "8", "9", "×"],
["4", "5", "6", "−"],
["1", "2", "3", "+"],
["±", "0", ".", "="],
couleurs = {
"=": ("#2E75B6", "white"),
"C": ("#e06c75", "white"),
"←": ("#e06c75", "white"),
"÷": ("#fab387", "#1e1e2e"),
"×": ("#fab387", "#1e1e2e"),
"−": ("#fab387", "#1e1e2e"),
"+": ("#fab387", "#1e1e2e"),
"%": ("#a6e3a1", "#1e1e2e"),
"±": ("#a6e3a1", "#1e1e2e"),
frame_boutons = [Link](fenetre, bg="#1e1e2e")
frame_boutons.pack(padx=15, pady=5, fill="both", expand=True)
for ligne in boutons:
frame_ligne = [Link](frame_boutons, bg="#1e1e2e")
frame_ligne.pack(fill="x", pady=3)
for label in ligne:
bg, fg = [Link](label, ("#45475a", "white"))
if label == "←":
cmd = lambda: [Link]([Link]()[:-1])
elif label == "±":
def cmd():
try:
[Link](str(-float([Link]())))
except: pass
elif label == "C":
cmd = effacer
elif label == "=":
cmd = calculer
else:
cmd = lambda v=label: appuyer(v)
[Link](
frame_ligne, text=label, font=("Arial", 16, "bold"),
bg=bg, fg=fg, bd=0, relief="flat", cursor="hand2",
activebackground=bg, activeforeground=fg,
command=cmd, width=5, height=2
).pack(side="left", padx=3, expand=True)
[Link]()
Chapitre 13 — threading — Exécution parallèle
Par défaut Python exécute le code ligne par ligne (séquentiel). Le threading permet de lancer
plusieurs tâches en même temps.
13.1 Créer et lancer un thread
python
import threading
import time
def tache(nom, duree):
print(f"[{nom}] Démarrage...")
[Link](duree) # simule un travail
print(f"[{nom}] Terminé après {duree}s")
# ── Sans threading : séquentiel (total ≈ 6s) ──
tache("A", 2)
tache("B", 3)
tache("C", 1)
# ── Avec threading : parallèle (total ≈ 3s) ──
t1 = [Link](target=tache, args=("A", 2))
t2 = [Link](target=tache, args=("B", 3))
t3 = [Link](target=tache, args=("C", 1))
[Link]()
[Link]()
[Link]()
# join() → attendre la fin d'un thread avant de continuer
[Link]()
[Link]()
[Link]()
print("Tous les threads sont terminés !")
13.2 Threads en tant que classe
python
import threading
import time
class TacheThread([Link]):
def __init__(self, nom, duree):
super().__init__()
[Link] = nom
[Link] = duree
def run(self): # s'exécute quand on appelle .start()
print(f"[{[Link]}] Démarrage")
[Link]([Link])
print(f"[{[Link]}] Fini !")
threads = [TacheThread(f"Thread-{i}", i) for i in range(1, 5)]
for t in threads:
[Link]()
for t in threads:
[Link]()
print("Tout est terminé.")
13.3 Le problème des ressources partagées — Lock
Quand plusieurs threads accèdent à la même variable, des conflits peuvent apparaître (race
condition). Le Lock garantit qu'un seul thread accède à la ressource à la fois.
python
import threading
compteur = 0
lock = [Link]()
def incrementer(n):
global compteur
for _ in range(n):
with lock: # un seul thread à la fois dans ce bloc
compteur += 1
threads = [[Link](target=incrementer, args=(100000,)) for _ in range(5)]
for t in threads: [Link]()
for t in threads: [Link]()
print(f"Compteur final : {compteur}") # doit afficher 500000
# Sans lock → résultat aléatoire et incorrect !
13.4 Partager des données entre threads — Queue
La Queue est la façon propre de faire communiquer des threads sans conflit.
python
import threading
import queue
import time
import random
file = [Link]()
def producteur(nb_items):
for i in range(nb_items):
item = f"item_{i}"
[Link](item)
print(f"Produit : {item}")
[Link]([Link](0.1, 0.3))
[Link](None) # signal de fin
def consommateur():
while True:
item = [Link]()
if item is None:
break
print(f" ↳ Consommé : {item}")
[Link]([Link](0.2, 0.5))
file.task_done()
t_prod = [Link](target=producteur, args=(6,))
t_cons = [Link](target=consommateur)
t_prod.start()
t_cons.start()
t_prod.join()
t_cons.join()
print("Pipeline terminé ")
13.5 Threading + tkinter — Interface non bloquante
C'est le cas d'usage le plus courant : lancer une tâche longue sans geler l'interface.
python
import tkinter as tk
import threading
import time
fenetre = [Link]()
[Link]("Threading + tkinter")
[Link]("350x200")
label = [Link](fenetre, text="Prêt.", font=("Arial", 14))
[Link](pady=20)
barre = [Link](fenetre, text="", font=("Arial", 20))
[Link]()
def tache_longue():
for i in range(1, 11):
[Link](0.5)
# Mettre à jour l'UI depuis un thread → utiliser after() ou StringVar
[Link](0, lambda i=i: [Link](
text=f"Traitement... {i * 10}%"
))
[Link](0, lambda i=i: [Link](
text="█" * i + "░" * (10 - i)
))
[Link](0, lambda: [Link](text="Terminé "))
[Link](0, lambda: [Link](state="normal"))
def lancer():
[Link](state="disabled")
[Link](text="Démarrage...")
t = [Link](target=tache_longue, daemon=True)
[Link]()
bouton = [Link](fenetre, text="Lancer la tâche",
command=lancer, bg="#2E75B6", fg="white",
font=("Arial", 12))
[Link](pady=15)
[Link]()
Ne jamais modifier les widgets tkinter directement depuis un thread. Toujours utiliser
[Link](0, callback) qui remet la mise à jour dans le thread principal.
Exercice 14 — Téléchargeur simulé avec interface
Crée une application tkinter qui :
1. Affiche une liste de 5 "fichiers" à télécharger (noms + tailles fictives).
2. Un bouton "Tout télécharger" lance les téléchargements en parallèle (threads).
3. Chaque fichier a sa propre barre de progression (Label texte).
4. Un label global indique quand tout est terminé.
Solution :
python
import tkinter as tk
import threading
import time
import random
FICHIERS = [
("rapport_annuel.pdf", 2.4),
("video_cours.mp4", 15.7),
("[Link]", 5.1),
("musique.mp3", 3.8),
("[Link]", 9.2),
]
fenetre = [Link]()
[Link]("Téléchargeur")
[Link]("450x370")
[Link](bg="#1e1e2e")
[Link](fenetre, text="Téléchargeur de fichiers",
font=("Arial", 15, "bold"), bg="#1e1e2e", fg="white").pack(pady=10)
barres = {} # {nom: (label_nom, label_barre, label_pct)}
frame = [Link](fenetre, bg="#1e1e2e")
[Link](fill="x", padx=20)
for nom, taille in FICHIERS:
f = [Link](frame, bg="#313244", pady=6)
[Link](fill="x", pady=3)
[Link](f, text=f"{nom} ({taille} Mo)",
bg="#313244", fg="#cdd6f4", font=("Arial", 10), width=35,
anchor="w").pack(side="left", padx=8)
lbl_barre = [Link](f, text="░"*20, bg="#313244",
fg="#6c7086", font=("Courier", 10))
lbl_barre.pack(side="left")
lbl_pct = [Link](f, text=" 0%", bg="#313244",
fg="#6c7086", font=("Arial", 10), width=5)
lbl_pct.pack(side="left")
barres[nom] = (lbl_barre, lbl_pct)
statut = [Link](fenetre, text="", font=("Arial", 11),
bg="#1e1e2e", fg="#a6e3a1")
[Link](pady=8)
def telecharger(nom, taille):
etapes = 20
for i in range(1, etapes + 1):
# Simuler une vitesse variable
[Link](taille / etapes * [Link](0.05, 0.15))
rempli = "█" * i + "░" * (etapes - i)
pct = f"{i * 5}%"
lbl_barre, lbl_pct = barres[nom]
[Link](0, lambda b=rempli, lb=lbl_barre: [Link](
text=b, fg="#89b4fa"))
[Link](0, lambda p=pct, lp=lbl_pct: [Link](
text=f" {p}", fg="#89b4fa"))
# Marquer comme terminé
[Link](0, lambda lb=barres[nom][0]: [Link](fg="#a6e3a1"))
[Link](0, lambda lp=barres[nom][1]: [Link](fg="#a6e3a1"))
def tout_telecharger():
[Link](state="disabled")
[Link](text="Téléchargement en cours...", fg="#f9e2af")
threads = []
for nom, taille in FICHIERS:
t = [Link](target=telecharger, args=(nom, taille), daemon=True)
[Link](t)
[Link]()
def attendre():
for t in threads:
[Link]()
[Link](0, lambda: [Link](
text="Tous les fichiers téléchargés ", fg="#a6e3a1"))
[Link](target=attendre, daemon=True).start()
bouton = [Link](fenetre, text="⬇ Tout télécharger",
command=tout_telecharger, font=("Arial", 12, "bold"),
bg="#2E75B6", fg="white", bd=0, padx=15, pady=8,
cursor="hand2", activebackground="#1a5a9e")
[Link](pady=5)
[Link]()
Récapitulatif — Les 4 modules
Module Utilité principale Fonctions clés
os Système de fichiers, chemins, env getcwd, listdir, walk, [Link], makedirs
json Sérialisation, config, API load, dump, loads, dumps
tkinter Interfaces graphiques Tk, Label, Button, Entry, mainloop
threading Parallélisme, UI non bloquante Thread, Lock, Queue, start, join
Prochaines étapes naturelles depuis ces modules :
• pathlib → alternative moderne et orientée objet à [Link]
• requests → HTTP et API REST (JSON en pratique réelle)
• [Link] + thèmes → UI plus moderne
• asyncio → alternative au threading pour les tâches I/O
Peut tu ajouter socket
08:59
Chapitre 14 — Le module socket — Communication réseau
Les sockets permettent à deux programmes de communiquer via le réseau, que ce soit sur la
même machine ou entre deux ordinateurs distants. C'est la base de tout ce qui tourne sur
internet : HTTP, FTP, SSH, etc.
14.1 Comprendre les sockets
┌─────────────────┐ réseau ┌─────────────────┐
│ CLIENT │ ──────────────────► │ SERVEUR │
│ │ ◄────────────────── │ │
│ [Link] │ TCP/IP │ [Link] │
│ [Link] │ │ [Link] │
│ [Link] │ │ [Link] │
└─────────────────┘ └─────────────────┘
Deux protocoles principaux :
• TCP (SOCK_STREAM) — fiable, ordonné, avec accusé de réception. Utilisé pour HTTP,
SSH, FTP.
• UDP (SOCK_DGRAM) — rapide, sans connexion, sans garantie. Utilisé pour le
streaming, les jeux en ligne.
14.2 Serveur TCP minimal
python
import socket
# Créer le socket
serveur = [Link](socket.AF_INET, # IPv4
socket.SOCK_STREAM) # TCP
# Évite l'erreur "Address already in use" au redémarrage
[Link](socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Associer à une adresse et un port
[Link](("localhost", 12345))
# Écouter (max 5 connexions en attente)
[Link](5)
print("Serveur en écoute sur le port 12345...")
# Accepter une connexion
client_socket, adresse = [Link]()
print(f"Connexion de {adresse}")
# Recevoir des données (max 1024 octets)
donnees = client_socket.recv(1024)
print(f"Reçu : {[Link]('utf-8')}")
# Répondre
client_socket.send("Message bien reçu !".encode("utf-8"))
# Fermer
client_socket.close()
[Link]()
14.3 Client TCP minimal
python
import socket
# Créer le socket
client = [Link](socket.AF_INET, socket.SOCK_STREAM)
# Se connecter au serveur
[Link](("localhost", 12345))
# Envoyer un message (toujours encoder en bytes)
[Link]("Bonjour serveur !".encode("utf-8"))
# Recevoir la réponse
reponse = [Link](1024)
print(f"Réponse : {[Link]('utf-8')}")
# Fermer
[Link]()
Les sockets transmettent des bytes, pas des strings. Il faut toujours .encode("utf-8") pour
envoyer et .decode("utf-8") pour recevoir.
14.4 Serveur multi-clients avec threading
Un serveur réel doit gérer plusieurs clients simultanément. On utilise un thread par client.
python
import socket
import threading
def gerer_client(conn, adresse):
print(f"[+] Nouveau client : {adresse}")
try:
while True:
donnees = [Link](1024)
if not donnees:
break # client déconnecté
message = [Link]("utf-8")
print(f"[{adresse}] → {message}")
# Renvoyer le message en majuscules (echo transformé)
reponse = f"ECHO : {[Link]()}"
[Link]([Link]("utf-8"))
except ConnectionResetError:
print(f"[!] {adresse} s'est déconnecté brutalement.")
finally:
[Link]()
print(f"[-] {adresse} déconnecté.")
def lancer_serveur(hote="localhost", port=12345):
serveur = [Link](socket.AF_INET, socket.SOCK_STREAM)
[Link](socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
[Link]((hote, port))
[Link](10)
print(f"Serveur démarré sur {hote}:{port}")
print("En attente de connexions... (Ctrl+C pour arrêter)\n")
try:
while True:
conn, adresse = [Link]()
# Un thread par client
t = [Link](
target=gerer_client,
args=(conn, adresse),
daemon=True
[Link]()
print(f"Threads actifs : {threading.active_count() - 1}")
except KeyboardInterrupt:
print("\nServeur arrêté.")
finally:
[Link]()
lancer_serveur()
14.5 Client interactif
python
import socket
import threading
def recevoir(sock):
"""Thread dédié à la réception des messages."""
while True:
try:
msg = [Link](1024).decode("utf-8")
if not msg:
print("Connexion fermée par le serveur.")
break
print(f"\r{msg}\nToi > ", end="", flush=True)
except:
break
def lancer_client(hote="localhost", port=12345):
client = [Link](socket.AF_INET, socket.SOCK_STREAM)
try:
[Link]((hote, port))
print(f"Connecté à {hote}:{port}")
print("Tape 'quit' pour quitter.\n")
except ConnectionRefusedError:
print("Impossible de se connecter. Le serveur est-il lancé ?")
return
# Thread de réception en arrière-plan
t = [Link](target=recevoir, args=(client,), daemon=True)
[Link]()
# Boucle d'envoi
try:
while True:
msg = input("Toi > ")
if [Link]() == "quit":
break
if msg:
[Link]([Link]("utf-8"))
except KeyboardInterrupt:
pass
finally:
[Link]()
print("Déconnecté.")
lancer_client()
14.6 Envoyer des données structurées — JSON sur socket
En pratique, on envoie rarement du texte brut. On envoie du JSON pour structurer les
données, mais il faut gérer la délimitation des messages (un recv peut recevoir plusieurs
messages à la fois ou un message en plusieurs morceaux).
La solution la plus simple : préfixer chaque message par sa longueur sur 4 octets.
python
import socket
import json
import struct
def envoyer_json(sock, data):
"""Envoie un objet Python comme JSON, préfixé par sa longueur."""
message = [Link](data, ensure_ascii=False).encode("utf-8")
# [Link](">I", n) → 4 octets big-endian représentant n
longueur = [Link](">I", len(message))
[Link](longueur + message)
def recevoir_json(sock):
"""Reçoit un message JSON complet et retourne l'objet Python."""
# Lire exactement 4 octets de longueur
raw_len = recvall(sock, 4)
if not raw_len:
return None
longueur = [Link](">I", raw_len)[0]
# Lire exactement `longueur` octets
raw_data = recvall(sock, longueur)
return [Link](raw_data.decode("utf-8"))
def recvall(sock, n):
"""Reçoit exactement n octets (plusieurs recv si nécessaire)."""
data = b""
while len(data) < n:
paquet = [Link](n - len(data))
if not paquet:
return None
data += paquet
return data
# ── Exemple d'utilisation côté serveur ──
def gerer_client_json(conn, adresse):
print(f"[+] {adresse} connecté")
while True:
data = recevoir_json(conn)
if not data:
break
print(f"[{adresse}] reçu : {data}")
# Répondre avec un objet structuré
reponse = {
"statut": "ok",
"echo": data,
"traite": True
envoyer_json(conn, reponse)
[Link]()
print(f"[-] {adresse} déconnecté")
14.7 Socket UDP
UDP est sans connexion et plus rapide. Parfait pour les données en temps réel où on peut se
permettre de perdre quelques paquets.
python
import socket
# ── Serveur UDP ──
def serveur_udp(port=9999):
sock = [Link](socket.AF_INET, socket.SOCK_DGRAM)
[Link](("localhost", port))
print(f"Serveur UDP sur le port {port}")
while True:
donnees, adresse = [Link](1024) # pas d'accept() en UDP !
msg = [Link]("utf-8")
print(f"[{adresse}] {msg}")
[Link](f"Reçu : {msg}".encode("utf-8"), adresse)
# ── Client UDP ──
def client_udp(port=9999):
sock = [Link](socket.AF_INET, socket.SOCK_DGRAM)
for i in range(5):
msg = f"Paquet UDP n°{i}"
[Link]([Link]("utf-8"), ("localhost", port)) # pas de connect()
reponse, _ = [Link](1024)
print(f"Réponse : {[Link]('utf-8')}")
[Link]()
14.8 Gestionnaire de contexte et bonnes pratiques
python
import socket
# ── Utiliser with pour fermeture automatique ──
with [Link](socket.AF_INET, socket.SOCK_STREAM) as client:
[Link](("localhost", 12345))
[Link]("Bonjour !".encode("utf-8"))
reponse = [Link](1024)
print([Link]("utf-8"))
# socket fermé automatiquement ici, même en cas d'erreur
# ── Timeout : ne pas bloquer indéfiniment ──
with [Link](socket.AF_INET, socket.SOCK_STREAM) as sock:
[Link](5.0) # lève [Link] après 5 secondes
try:
[Link](("[Link]", 80))
[Link](b"GET / HTTP/1.0\r\nHost: [Link]\r\n\r\n")
reponse = [Link](4096)
print(reponse[:200])
except [Link]:
print("Timeout : le serveur ne répond pas.")
except ConnectionRefusedError:
print("Connexion refusée.")
except [Link] as e:
print(f"Erreur DNS : {e}")
# ── Trouver un port libre automatiquement ──
def trouver_port_libre():
with [Link](socket.AF_INET, socket.SOCK_STREAM) as s:
[Link](("", 0))
return [Link]()[1]
port = trouver_port_libre()
print(f"Port libre trouvé : {port}")
# ── Obtenir l'IP locale et le hostname ──
hostname = [Link]()
ip_locale = [Link](hostname)
print(f"Machine : {hostname} ({ip_locale})")
Exercice 15 — Chat en réseau
Crée une application de chat en temps réel entre plusieurs clients :
Serveur :
• Accepte plusieurs clients simultanément (threads)
• Chaque client envoie son pseudo à la connexion
• Quand un client envoie un message, le serveur le diffuse à tous les autres (broadcast)
• Affiche [pseudo] a rejoint le chat et [pseudo] a quitté le chat
Client :
• Demande le pseudo au démarrage
• Thread dédié à la réception (affichage en temps réel)
• Thread principal pour la saisie et l'envoi
• Commande /quit pour se déconnecter proprement
Solution — Serveur :
python
import socket
import threading
clients = {} # {socket: pseudo}
lock_cli = [Link]()
def broadcast(message, expediteur=None):
"""Envoie un message à tous les clients sauf l'expéditeur."""
with lock_cli:
for sock in list(clients):
if sock != expediteur:
try:
[Link]([Link]("utf-8"))
except:
[Link]()
del clients[sock]
def gerer_client(conn, adresse):
try:
# Premier message = pseudo
pseudo = [Link](1024).decode("utf-8").strip()
with lock_cli:
clients[conn] = pseudo
print(f"[+] {pseudo} ({adresse})")
broadcast(f"★ {pseudo} a rejoint le chat !", expediteur=conn)
[Link]("Bienvenue ! Tape /quit pour partir.\n".encode("utf-8"))
while True:
msg = [Link](1024).decode("utf-8").strip()
if not msg or msg == "/quit":
break
print(f"[{pseudo}] {msg}")
broadcast(f"[{pseudo}] {msg}", expediteur=conn)
except (ConnectionResetError, ConnectionAbortedError):
pass
finally:
pseudo = [Link](conn, "Inconnu")
with lock_cli:
if conn in clients:
del clients[conn]
[Link]()
print(f"[-] {pseudo} déconnecté")
broadcast(f"★ {pseudo} a quitté le chat.")
def lancer_serveur(port=5555):
serveur = [Link](socket.AF_INET, socket.SOCK_STREAM)
[Link](socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
[Link](("localhost", port))
[Link](10)
print(f"Serveur chat sur le port {port}\n")
try:
while True:
conn, adresse = [Link]()
[Link](target=gerer_client,
args=(conn, adresse), daemon=True).start()
except KeyboardInterrupt:
print("\nServeur arrêté.")
finally:
[Link]()
lancer_serveur()
Solution — Client :
python
import socket
import threading
def recevoir(sock):
while True:
try:
msg = [Link](1024).decode("utf-8")
if not msg:
print("\nDéconnecté du serveur.")
break
print(f"\r{msg}")
print(f"Toi > ", end="", flush=True)
except:
break
def lancer_client(port=5555):
sock = [Link](socket.AF_INET, socket.SOCK_STREAM)
try:
[Link](("localhost", port))
except ConnectionRefusedError:
print("Serveur introuvable. Lance le serveur d'abord !")
return
pseudo = input("Ton pseudo : ").strip()
[Link]([Link]("utf-8"))
[Link](target=recevoir, args=(sock,), daemon=True).start()
try:
while True:
msg = input("Toi > ").strip()
if not msg:
continue
[Link]([Link]("utf-8"))
if msg == "/quit":
break
except KeyboardInterrupt:
[Link]("/quit".encode("utf-8"))
finally:
[Link]()
print("Déconnecté.")
lancer_client()
Pour tester : lance [Link] dans un terminal, puis lance [Link] dans plusieurs autres
terminaux simultanément.
Récapitulatif — socket
Concept Rôle
socket() Créer un socket TCP ou UDP
bind() Associer à une adresse + port (serveur)
listen() / accept() Attendre et accepter une connexion TCP
Concept Rôle
connect() Se connecter à un serveur (client)
send() / recv() Envoyer / recevoir des bytes
sendall() Envoyer garantissant l'envoi complet
sendto() / recvfrom() Envoyer / recevoir en UDP (sans connexion)
close() Fermer la connexion
settimeout() Définir un délai d'attente maximum
[Link] Un thread par client pour le multi-client
[Link] Préfixer la longueur pour les messages JSON