La Programmation Orientée Objet (POO) est le tournant intellectuel du cursus Python. Elle change votre façon de concevoir un programme : on ne manipule plus seulement des données, on modélise des objets qui combinent données et comportements. Prenez votre temps — ce chapitre est dense mais fondamental.
33.1 Pourquoi la POO ?
Imaginez que vous développez un jeu vidéo avec des personnages. Chaque personnage a :
Des données : nom, points de vie, force, équipement…
Des comportements : attaquer, se défendre, soigner, mourir…
Sans POO, il faudrait gérer tout ça avec des dictionnaires et des fonctions séparées :
N’importe qui peut modifier directement les PV (personnage["pv"] = 9999).
Pas de réutilisation : si on veut un nouveau type de personnage, on recopie tout.
Pas de contrat : rien ne garantit qu’un « personnage » a bien les clés pv et force.
Avec la POO, on regroupe données + comportements dans une classe :
class Personnage:def__init__(self, nom, pv, force):self.nom = nomself.pv = pvself.force = forcedef attaquer(self, cible): cible.pv -=self.force# Création de personnagesaragorn = Personnage("Aragorn", 100, 20)legolas = Personnage("Legolas", 80, 15)# Utilisationaragorn.attaquer(legolas)print(f"{legolas.nom} a maintenant {legolas.pv} PV")
Legolas a maintenant 60 PV
Plus propre, plus sûr, plus extensible.
33.2 Vocabulaire essentiel
Terme
Définition
Classe
Le moule — définit le type d’objet (ses attributs, ses méthodes).
Instance
Un objet concret créé à partir de la classe.
Attribut
Une donnée stockée dans une instance ou une classe.
Méthode
Une fonction définie dans une classe, appelée sur une instance.
self
Référence à l’instance courante dans les méthodes.
Constructeur
La méthode spéciale __init__ qui initialise un nouvel objet.
Analogie
Une classe = le plan d’une maison.
Une instance = une maison construite selon ce plan.
Les attributs = couleur de la façade, nombre de pièces (propres à chaque maison).
Les méthodes = actions possibles (ouvrir la porte, allumer la lumière).
33.3 Définir une classe
class NomDeClasse:# corps de la classe ...
Convention PEP 8 : nom de classe en PascalCase (première lettre de chaque mot en majuscule, sans underscore).
class Chien:pass# classe vide pour l'instant# Créer des instancesrex = Chien()medor = Chien()print(rex)print(type(rex))print(rex is medor) # False : deux instances distinctes
<__main__.Chien object at 0x000001ED78DF7A40>
<class '__main__.Chien'>
False
Les parenthèses () à l’appel Chien()instancient la classe (créent un nouvel objet).
33.4 Les attributs d’instance
Un attribut d’instance est une donnée propre à chaque objet. On les ajoute avec instance.attribut = valeur.
class Chien:passrex = Chien()rex.nom ="Rex"rex.age =3rex.race ="Berger allemand"medor = Chien()medor.nom ="Médor"medor.age =7print(f"{rex.nom}, {rex.age} ans, {rex.race}")print(f"{medor.nom}, {medor.age} ans")
Rex, 3 ans, Berger allemand
Médor, 7 ans
Chaque instance a ses propres attributs — rex.nom et medor.nom sont indépendants.
Limite de cette approche
Écrire rex.nom = ...; rex.age = ... à chaque création est fastidieux et risqué : on peut oublier un attribut, ou faire une faute de frappe.
C’est le rôle du constructeur __init__.
33.5 Le constructeur __init__
__init__ (prononcé « dunder init », pour double underscore init) est une méthode spéciale appelée automatiquement à la création d’une instance. Elle sert à initialiser les attributs.
class Chien:def__init__(self, nom, age, race="inconnue"):self.nom = nomself.age = ageself.race = race# Création — les arguments passent au __init__rex = Chien("Rex", 3, "Berger allemand")medor = Chien("Médor", 7) # race prend sa valeur par défautprint(f"{rex.nom}, {rex.age} ans, {rex.race}")print(f"{medor.nom}, {medor.age} ans, {medor.race}")
Rex, 3 ans, Berger allemand
Médor, 7 ans, inconnue
__init__ne se return pas une valeur : Python renvoie l’instance automatiquement.
Les paramètres après self sont ceux qu’on passe à l’appel : Chien("Rex", 3).
self.nom = nomstocke le paramètre dans l’attribut.
33.6 Le paramètre self — clé de la POO
self est le paramètre le plus déroutant de la POO Python. Voici l’essentiel à comprendre :
Qu’est-ce que self ?
self est une convention : c’est la première variable que Python passe automatiquement aux méthodes, et elle désigne l’instance courante sur laquelle la méthode est appelée.
class Chien:def__init__(self, nom):self.nom = nomdef aboyer(self):print(f"{self.nom} dit : Wouf !")rex = Chien("Rex")rex.aboyer() # équivalent à Chien.aboyer(rex)
Rex dit : Wouf !
Quand on écrit rex.aboyer(), Python transforme ça en interne en Chien.aboyer(rex) — c’est pourquoi self est rempli automatiquement par rex.
Pourquoi écrire self ?
Sans self, une méthode ne saurait pas quelle instance utiliser. Imaginez deux chiens :
class Chien:def__init__(self, nom):self.nom = nomdef aboyer(self):print(f"{self.nom} dit : Wouf !")rex = Chien("Rex")medor = Chien("Médor")rex.aboyer() # self = rex → affiche "Rex"medor.aboyer() # self = medor → affiche "Médor"
Rex dit : Wouf !
Médor dit : Wouf !
Le même code de aboyer, mais self change selon l’instance.
self est une convention, pas un mot-clé
Techniquement, vous pourriez écrire def aboyer(this): — ça marcherait. Mais TOUS les développeurs Python utilisent self. L’utiliser rend votre code immédiatement lisible. Respectez la convention.
33.7 Les méthodes
Une méthode est une fonction définie dans une classe. Sa première paramètre est toujours self.
Compte de Alice : 100 €
+50 €. Nouveau solde : 150 €
-30 €. Nouveau solde : 120 €
Fonds insuffisants
Compte de Alice : 120 €
Points à noter :
Toutes les méthodes ont self comme premier paramètre.
On appelle avec instance.methode(arguments).
Dans la méthode, on lit et modifie les attributs via self.attribut.
33.8 Attributs de classe vs attributs d’instance
Il existe deux types d’attributs :
Attributs d’instance
Déclarés dans __init__ avec self.attribut = ... — propres à chaque objet.
class Chien:def__init__(self, nom):self.nom = nom # attribut d'instancerex = Chien("Rex")medor = Chien("Médor")print(rex.nom, medor.nom) # noms différents
Rex Médor
Attributs de classe
Déclarés directement dans le corps de la classe, en dehors des méthodes — partagés par toutes les instances.
class Chien: espece ="Canis lupus familiaris"# attribut de CLASSE, partagédef__init__(self, nom):self.nom = nom # attribut d'instancerex = Chien("Rex")medor = Chien("Médor")print(rex.nom, rex.espece)print(medor.nom, medor.espece)# Accès via la classe directementprint(Chien.espece)
Par défaut, print(objet) affiche quelque chose de peu lisible :
class Chien:def__init__(self, nom):self.nom = nomrex = Chien("Rex")print(rex)
<__main__.Chien object at 0x000001ED7840EAB0>
La méthode spéciale __str__ permet de définir une représentation humaine :
class Chien:def__init__(self, nom):self.nom = nomdef__str__(self):returnf"Chien nommé {self.nom}"rex = Chien("Rex")print(rex) # affiche "Chien nommé Rex"print(str(rex))
Chien nommé Rex
Chien nommé Rex
On verra plus de méthodes spéciales (dites « dunders ») dans le chapitre suivant.
33.11 Tester le type : isinstance
Pour tester si un objet est une instance d’une classe :
class Chien:passclass Chat:passrex = Chien()print(isinstance(rex, Chien)) # Trueprint(isinstance(rex, Chat)) # Falseprint(isinstance(rex, (Chien, Chat))) # True si l'un OU l'autre
True
False
True
isinstance est préféré à type(rex) == Chien — il gère correctement les sous-classes (vu en chapitre 5).
🧩 Quiz 4.1 — POO partie 1
Question 1
Quel est le nom de la méthode spéciale appelée à la création d’une instance ?
__new__
__init__
__create__
constructor
🔍 Réponse
b) __init__ — c’est la méthode d’initialisation, appelée juste après la création d’un objet. __new__ existe aussi mais est rarement utilisé directement.
Question 2
Que désigne le paramètre self dans une méthode ?
La classe elle-même
L’instance courante
Le premier argument passé à la méthode
Un mot-clé Python
🔍 Réponse
b) L’instance courante — self est une convention qui désigne l’objet sur lequel la méthode est appelée. Python le remplit automatiquement.
Question 3
Que s’affiche-t-il ?
class C:def__init__(self, x):self.x = xa = C(10)b = C(20)print(a.x + b.x)
10
20
30
Une erreur
🔍 Réponse
c) 30 — a.x = 10, b.x = 20, chaque instance a ses propres attributs.
class C:def__init__(self, x):self.x = xa = C(10)b = C(20)print(a.x + b.x)
30
Question 4
Quelle convention PEP 8 pour un nom de classe ?
snake_case : mon_objet
camelCase : monObjet
PascalCase : MonObjet
SCREAMING_SNAKE : MON_OBJET
🔍 Réponse
c) PascalCase — première lettre de chaque mot en majuscule, sans underscore. Ex: CompteBancaire, Utilisateur, Rectangle.
b) ["pain"] — piège TOSA ! articles est un attribut de classe mutable, partagé par toutes les instances. Il faut l’initialiser dans __init__ pour éviter ce problème.
c) isinstance(obj, Chien) — méthode recommandée, qui gère aussi l’héritage (une sous-classe reste une instance de la classe parente). Les autres formes marchent mais sont moins idiomatiques.
Question 7
Que renvoie print(rex) avec cette classe ?
class Chien:def__init__(self, nom):self.nom = nomdef__str__(self):returnf"Chien : {self.nom}"rex = Chien("Rex")print(rex)
<__main__.Chien object at 0x...>
Chien : Rex
{"nom": "Rex"}
Une erreur
🔍 Réponse
b) Chien : Rex — print utilise __str__ si elle est définie. Sinon, on aurait la forme a).
class Chien:def__init__(self, nom):self.nom = nomdef__str__(self):returnf"Chien : {self.nom}"rex = Chien("Rex")print(rex)
Chien : Rex
✏️ Exercice 4.1 — Classe Rectangle
Créez une classe Rectangle avec :
Un constructeur prenant longueur et largeur.
Des méthodes aire(), perimetre(), est_carre().
Une méthode __str__ renvoyant "Rectangle 4x3 (aire = 12)".
class Livre: ...l1 = Livre("Le Petit Prince", "Saint-Exupéry", 1943)l2 = Livre("1984", "Orwell", 1949)l3 = Livre("L'Étranger", "Camus", 1942)print(l1)print(l2)print(Livre.nb_livres) # 3
class Livre: nb_livres =0# attribut de CLASSE, partagédef__init__(self, titre, auteur, annee):self.titre = titreself.auteur = auteurself.annee = annee Livre.nb_livres +=1# incrémente à chaque créationdef__str__(self):returnf"'{self.titre}' par {self.auteur} ({self.annee})"l1 = Livre("Le Petit Prince", "Saint-Exupéry", 1943)l2 = Livre("1984", "Orwell", 1949)l3 = Livre("L'Étranger", "Camus", 1942)print(l1)print(l2)print(l3)print(f"Nombre total de livres : {Livre.nb_livres}")
'Le Petit Prince' par Saint-Exupéry (1943)
'1984' par Orwell (1949)
'L'Étranger' par Camus (1942)
Nombre total de livres : 3
Faire self.nb_livres += 1créerait un attribut d’instance qui masquerait l’attribut de classe. Résultat : le compteur ne s’incrémenterait pas globalement.
class LivreBug: nb_livres =0def__init__(self, titre):self.titre = titreself.nb_livres +=1# ❌ crée un attribut d'instance !l1 = LivreBug("A")l2 = LivreBug("B")print("Classe :", LivreBug.nb_livres) # 0 — non modifié !print("l1 :", l1.nb_livres) # 1 — attribut local
Classe : 0
l1 : 1
✏️ Exercice 4.4 — Classe Etudiant avec bulletin
Créez une classe Etudiant :
Constructeur : nom, initialise une liste vide notes.
ajouter_note(note) : ajoute une note à la liste.
moyenne() : renvoie la moyenne (0 si aucune note).
mention() : renvoie "Très bien" si ≥16, "Bien" si ≥14, "Passable" si ≥10, "Insuffisant" sinon.
class Etudiant: ...alice = Etudiant("Alice")alice.ajouter_note(14)alice.ajouter_note(15)alice.ajouter_note(12)print(alice.moyenne())print(alice.mention())print(alice)
class Etudiant:def__init__(self, nom):self.nom = nomself.notes = [] # CHAQUE instance a SA listedef ajouter_note(self, note):if0<= note <=20:self.notes.append(note)else:print(f"Note invalide : {note}")def moyenne(self):ifnotself.notes:return0returnsum(self.notes) /len(self.notes)def mention(self): m =self.moyenne()if m >=16:return"Très bien"elif m >=14:return"Bien"elif m >=10:return"Passable"else:return"Insuffisant"def__str__(self):returnf"{self.nom} — {self.moyenne():.1f} ({self.mention()})"# Testsalice = Etudiant("Alice")for note in [14, 15, 12, 18]: alice.ajouter_note(note)bob = Etudiant("Bob")for note in [8, 7, 9]: bob.ajouter_note(note)charlie = Etudiant("Charlie") # aucune noteprint(alice)print(bob)print(charlie)
Alice — 14.8 (Bien)
Bob — 8.0 (Insuffisant)
Charlie — 0.0 (Insuffisant)
self.notes = [] dans __init__ — chaque étudiant a sa liste de notes (pas de partage entre instances).
Une méthode peut appeler une autre méthode de la même classe : mention() utilise moyenne().
__str__ peut utiliser les autres méthodes et faire du formatage riche.
À retenir
Points clés du chapitre
Une classe est un moule, une instance est un objet créé à partir de cette classe.
__init__(self, ...) est le constructeur, appelé automatiquement à la création.
self désigne l’instance courante — toujours premier paramètre des méthodes.
Attributs d’instance (self.x = ... dans __init__) : propres à chaque objet.
Attributs de classe (déclarés dans le corps) : partagés par toutes les instances.
JAMAIS d’attribut de classe mutable : utilisez __init__ pour les listes/dicts.
__str__ pour une représentation lisible par l’humain (utilisée par print).