Module 2Connaître les principaux piliers de la POO
Chapitre 3 sur 10
Définir l'héritage et le polymorphisme
Réutiliser du code par l'héritage et faire réagir différemment des objets au même message.
- ≈ 135 min
- 5 leçons
- 1 vidéo
- 3 exercices
Module 2Connaître les principaux piliers de la POO
Chapitre 3 sur 10
Réutiliser du code par l'héritage et faire réagir différemment des objets au même message.
Objectifs
Avec ce chapitre, tu sauras…
Leçon 1
Tableau: Le problème : la duplication (exemple du résumé OFPPT)
| Personne | Etudiant | Employe |
|---|---|---|
| nom, CIN, anneeNaiss | nom, CIN, anneeNaiss, note1, note2 | nom, CIN, anneeNaiss, prixHeure, nbreHeure |
| age() | age(), moyenne() | age(), salaire() |
Le nom, le CIN, l'année de naissance et la méthode age() sont écrits trois fois. Une modification (par exemple le calcul de l'âge) devrait être refaite dans les trois classes.
La solution : une classe mère
┌──────────────────────┐
│ Personne │ ← super-classe
│ nom, CIN, anneeNaiss │
│ age() │
└──────────┬───────────┘
hérite de │ hérite de
┌────────────────┴────────────────┐
┌─────────┴──────────┐ ┌─────────┴──────────┐
│ Etudiant │ │ Employe │ ← sous-classes
│ note1, note2 │ │ prixHeure, nbreHeure│
│ moyenne() │ │ salaire() │
└────────────────────┘ └────────────────────┘class Personne:
def __init__(self, nom, cin, annee_naiss):
self.nom = nom
self.cin = cin
self.annee_naiss = annee_naiss
def age(self):
return 2026 - self.annee_naiss
class Etudiant(Personne): # Etudiant hérite de Personne
def __init__(self, nom, cin, annee_naiss, note1, note2):
super().__init__(nom, cin, annee_naiss) # appelle le constructeur de la mère
self.note1 = note1
self.note2 = note2
def moyenne(self):
return (self.note1 + self.note2) / 2
class Employe(Personne):
def __init__(self, nom, cin, annee_naiss, prix_heure, nbre_heure):
super().__init__(nom, cin, annee_naiss)
self.prix_heure = prix_heure
self.nbre_heure = nbre_heure
def salaire(self):
return self.prix_heure * self.nbre_heure
e = Etudiant("Sara", "AB1234", 2005, 15, 17)
print(e.age(), e.moyenne()) # 21 16.0 : age() est héritéeTableau: L'intérêt de l'héritage
| Intérêt | Explication |
|---|---|
| moins de code | les caractéristiques communes sont écrites une seule fois, dans la classe de base |
| localisation | chaque section de code se trouve en un point unique |
| correction facile | une rectification se fait à un seul endroit |
| extension | ajouter une nouvelle classe (par exemple Formateur) est facile dans une hiérarchie bien conçue |
| modélisation | la hiérarchie rapproche le programme de la réalité |
Leçon 2
| Type | Principe | Exemple du résumé OFPPT |
|---|---|---|
| simple | une classe hérite d'une seule classe | Etudiant hérite de Personne |
| multiple | une classe hérite de plusieurs classes | Etudiant hérite de Personne et de EtreHumain |
| en cascade | une sous-classe est elle-même la super-classe d'une autre | EtudiantDeuxiemeCycle → Etudiant → Personne |
Leçon 3
Leçon 4
Leçon 5
| Affectation | Autorisée ? | Pourquoi |
|---|---|---|
p = e (p de type Personne, e de type Etudiant) | oui | un étudiant est une personne : le type de p est plus général |
e = p | non | une personne n'est pas forcément un étudiant |
1La classe dont on hérite est la classe…
2Si D hérite de B, un objet de D…
3Redéfinir une méthode, c'est…
4« Une voiture a un moteur » relève…
5Avec la liaison retardée, la méthode exécutée dépend…
6p = e (Personne p, Etudiant e) est…
L'essentiel du chapitre
class Fille(Mere): et super().__init__(...).super().methode() pour réutiliser la version mère.mere = fille autorisé, l'inverse non.À savoir
L'héritage multiple selon les langages
Python et le typage dynamique
Vidéo · À regarder
FRApprendre la Programmation Orientée Objets (POO) Python
MaxCode · YouTube
Pour savoir si une classe B doit hériter d'une classe A, pose la question : « un B est un A ? ». Un étudiant est une personne : héritage. Une voiture a un moteur : ce n'est pas de l'héritage mais de la composition (un attribut objet, chapitre 2).
class EtudiantDeuxiemeCycle(Etudiant): # cascade : hérite aussi de Personne
def __init__(self, nom, cin, annee_naiss, note1, note2, option):
super().__init__(nom, cin, annee_naiss, note1, note2)
self.option = option
class Sportif:
def courir(self):
return "Je cours"
class EtudiantSportif(Etudiant, Sportif): # multiple
passUne méthode héritée peut être redéfinie (override) dans la classe dérivée si sa version d'origine ne convient pas : on garde la même en-tête (même nom, mêmes paramètres) et on écrit un code différent. Seule la nouvelle version fait alors partie de la classe dérivée. Si la méthode de la classe dérivée a des paramètres différents, il s'agit d'une nouvelle méthode (dans les langages qui le permettent, on parle de surcharge).
class Personne:
def __init__(self, nom, cin):
self.nom = nom
self.cin = cin
def afficher(self):
print(f"Nom : {self.nom}, CIN : {self.cin}")
class Etudiant(Personne):
def __init__(self, nom, cin, note1, note2):
super().__init__(nom, cin)
self.note1 = note1
self.note2 = note2
def afficher(self): # redéfinition
super().afficher() # réutilise la version de la mère
print(f"Notes : {self.note1}, {self.note2}")
Etudiant("Sara", "AB1234", 15, 17).afficher()Quand on envoie le message o.m() à un objet o dont la classe réelle est C :
m(), c'est cette méthode qui est exécutée ;m().Le choix de la méthode se fait à l'exécution, selon la classe réelle de l'objet : d'où le nom de liaison retardée (ou dynamique).
Personne ── afficher()
▲
Etudiant ── afficher() (redéfinie, 1er cas) / rien (2e cas)
▲
EtudiantTroisiemeCycle ── afficher() redéfinie ?
etc.afficher() :
• si EtudiantTroisiemeCycle redéfinit afficher() → sa version est exécutée
• sinon, on remonte : Etudiant, puis Personne → la première version trouvée est exécutéeLe polymorphisme (« plusieurs formes ») est le fait qu'un même nom de variable puisse désigner des instances de classes différentes d'une même hiérarchie, et qu'un même message (afficher()) produise un comportement adapté à la classe réelle de chaque objet.
Tableau: Les affectations autorisées (langages à typage statique)
Dans les langages typés (Java, C#, C++), c'est le type déclaré de la variable qui détermine les membres accessibles. Si B hérite de A, avec A a2 = new B() : a2.methodeA() est accepté, mais a2.methodeB() est refusé à la compilation, même si l'objet est réellement un B.
personnes = [
Personne("Ali", "CD5678"),
Etudiant("Sara", "AB1234", 15, 17),
]
for p in personnes:
p.afficher() # chaque objet exécute SA version d'afficher()Qu'affiche ce code ? Justifie avec le mécanisme de la liaison retardée.
class A:
def qui(self):
return "A"
def presenter(self):
return "Je suis " + self.qui()
class B(A):
def qui(self):
return "B"
class C(B):
pass
print(A().presenter(), B().presenter(), C().presenter())Dessine une hiérarchie avec Vehicule (marque, vitesse_max, demarrer()), Voiture (nb_portes), Moto (type_casque) et VoitureElectrique (autonomie, recharger()). Indique les types d'héritage présents.
Écris en Python les classes Personne (nom, prénom, afficher()), Formateur (spécialité, salaire, redéfinit afficher()) et Stagiaire (filière, liste de notes, moyenne(), redéfinit afficher()). Crée une liste contenant un formateur et deux stagiaires, et affiche-les tous dans une boucle.
Python et C++ autorisent l'héritage multiple. Java et C# ne l'autorisent pas entre classes : ils utilisent les interfaces à la place (chapitre 5).
Python n'a pas de type déclaré pour les variables : n'importe quel objet qui possède une méthode afficher() peut être placé dans la liste et recevoir le message (on parle de duck typing : « si ça marche comme un canard et cancane comme un canard, c'est un canard »). La règle des affectations ci-dessus concerne les langages à typage statique comme Java ou C#.
Les méthodes de classe en Python : comment les méthodes s'écrivent et s'appellent dans une classe, base de la redéfinition.