Le chapitre précédent a posé les bases : classes, instances, attributs, méthodes. Ce chapitre approfondit les 3 grands piliers qui rendent la POO puissante : l'héritage (réutiliser du code), l'encapsulation (protéger les données), et les méthodes spéciales (faire dialoguer nos objets avec Python).
L'héritage
L'héritage permet de créer une nouvelle classe (fille) qui réutilise et étend une classe existante (mère). C'est le mécanisme clé de la réutilisation en POO.
Exemple introductif
Imaginons qu'après avoir créé Personnage, on veuille une classe Guerrier — un personnage spécialisé. Sans héritage, on devrait tout redupliquer. Avec héritage :
# Classe mère
class Personnage:
def __init__(self, nom, pv):
self.nom = nom
self.pv = pv
def attaquer(self, cible):
print(f"{self.nom} attaque {cible.nom}")
def __str__(self):
return f"{self.nom} ({self.pv} PV)"
# Classe fille : Guerrier EST UN Personnage
class Guerrier(Personnage):
def __init__(self, nom, pv, arme):
super().__init__(nom, pv) # appelle le __init__ du parent
self.arme = arme
def charger(self, cible):
print(f"{self.nom} charge {cible.nom} avec son {self.arme} !")
# Utilisation
aragorn = Guerrier("Aragorn", 100, "épée")
legolas = Personnage("Legolas", 80)
aragorn.attaquer(legolas) # hérité du parent
aragorn.charger(legolas) # spécifique à Guerrier
print(aragorn) # __str__ héritéRelation conceptuelle : Guerrier est un Personnage. On dit que Guerrier hérite de Personnage, ou qu'il en est une sous-classe.
Syntaxe
class ClasseFille(ClasseMere):
...Dans le corps, on peut :
- Ajouter de nouveaux attributs et méthodes.
- Redéfinir (override) des méthodes existantes.
- Appeler les méthodes du parent avec
super().
super() — accéder à la classe parente
super() permet d'appeler les méthodes du parent depuis la classe fille. Usage principal : dans __init__, pour initialiser d'abord ce que le parent fait, puis ajouter ses propres initialisations.
class Vehicule:
def __init__(self, marque, annee):
self.marque = marque
self.annee = annee
def presenter(self):
print(f"Véhicule {self.marque}, {self.annee}")
class Voiture(Vehicule):
def __init__(self, marque, annee, nb_portes):
super().__init__(marque, annee) # init du parent
self.nb_portes = nb_portes # spécifique à Voiture
def presenter(self):
super().presenter() # comportement parent
print(f" → {self.nb_portes} portes") # spécifique ajouté
v = Voiture("Renault", 2022, 5)
v.presenter()Redéfinir (override) une méthode
On peut remplacer une méthode du parent en la redéfinissant dans la fille.
class Animal:
def cri(self):
return "..."
class Chien(Animal):
def cri(self):
return "Wouf !"
class Chat(Animal):
def cri(self):
return "Miaou !"
class Canard(Animal):
def cri(self):
return "Coin coin"
# Polymorphisme : même appel, comportements différents
for animal in [Chien(), Chat(), Canard()]:
print(f"{type(animal).__name__} : {animal.cri()}")C'est ce qu'on appelle le polymorphisme : même interface (cri()), comportements différents selon le type.
Étendre une méthode (pas juste remplacer)
Parfois, on veut garder le comportement parent et ajouter quelque chose :
class Personnage:
def se_presenter(self):
print(f"Je suis {self.nom}")
class Heros(Personnage):
def __init__(self, nom, pouvoir):
self.nom = nom
self.pouvoir = pouvoir
def se_presenter(self):
super().se_presenter() # comportement parent d'abord
print(f" et j'ai le pouvoir : {self.pouvoir}")
h = Heros("Gandalf", "Magie")
h.se_presenter()Héritage multi-niveaux
On peut hériter d'une classe qui hérite elle-même d'une autre. La hiérarchie peut être profonde.
class Animal:
def __init__(self, nom):
self.nom = nom
class Mammifere(Animal):
def __init__(self, nom, poils):
super().__init__(nom)
self.poils = poils
class Chien(Mammifere):
def __init__(self, nom, race):
super().__init__(nom, poils=True)
self.race = race
def aboyer(self):
print(f"{self.nom} (race {self.race}) aboie !")
rex = Chien("Rex", "Berger")
rex.aboyer()
print(f"A des poils : {rex.poils}")isinstance et les hiérarchies
isinstance(obj, Classe) renvoie True pour toutes les classes parentes, pas juste la classe directe.
class Animal: pass
class Mammifere(Animal): pass
class Chien(Mammifere): pass
rex = Chien()
print(isinstance(rex, Chien)) # True
print(isinstance(rex, Mammifere)) # True (ancêtre)
print(isinstance(rex, Animal)) # True (ancêtre plus lointain)C'est le comportement attendu : un Chien est un Mammifere, est un Animal.