
Héritage : créer des classes filles à partir de classes mères
Découvrez le concept d'héritage en programmation orientée objet (POO) avec Python. Créez des classes filles (sous-classes) qui héritent des attributs et méthodes de classes mères (super-classes), et étendez ou spécialisez leur comportement.
Qu'est-ce que l'héritage ? Principe et avantages
L'héritage est l'un des quatre piliers de la programmation orientée objet (avec l'encapsulation, l'abstraction et le polymorphisme). C'est un mécanisme qui permet de créer une nouvelle classe (appelée classe fille, sous-classe, ou classe dérivée) à partir d'une classe existante (appelée classe mère, super-classe, ou classe de base).
La classe fille hérite des attributs et des méthodes de la classe mère. Cela signifie qu'elle a accès à toutes les données et à tous les comportements définis dans la classe mère, sans avoir à les réécrire.
La classe fille peut ensuite *étendre* ou *spécialiser* le comportement de la classe mère en :
- Ajoutant de nouveaux attributs.
- Ajoutant de nouvelles méthodes.
- Redéfinissant (surchargeant) des méthodes existantes.
L'héritage favorise :
- La réutilisabilité du code : Vous n'avez pas besoin de réécrire le code de la classe mère dans la classe fille.
- L'organisation du code : L'héritage permet de créer une hiérarchie de classes, qui reflète les relations entre les objets du monde réel.
- La maintenabilité : Si vous devez modifier le comportement de la classe mère, les modifications sont automatiquement répercutées sur les classes filles (sauf si elles ont redéfini les méthodes concernées).
- L'extensibilité : Il est facile d'ajouter de nouvelles fonctionnalités en créant de nouvelles classes filles.
Syntaxe de l'héritage : class Fille(Mère):
En Python, vous indiquez qu'une classe hérite d'une autre classe en plaçant le nom de la classe mère entre parenthèses après le nom de la classe fille, dans la définition de la classe fille.
Syntaxe :
class ClasseMere:
# ...
class ClasseFille(ClasseMere):
# ...Exemple :
class Animal:
def __init__(self, nom):
self.nom = nom
def manger(self):
print(f"{self.nom} mange.")
class Chien(Animal):
def aboyer(self):
print("Wouf wouf !")
class Chat(Animal):
def miauler(self):
print("Miaou !")
# Utilisation
mon_chien = Chien("Médor")
mon_chien.manger() # Hérité de Animal : affiche "Médor mange."
mon_chien.aboyer() # Spécifique à Chien : affiche "Wouf wouf !"
mon_chat = Chat("Félix")
mon_chat.manger() # Hérité de Animal : affiche "Félix mange."
mon_chat.miauler() # Spécifique à Chat : affiche "Miaou !"Dans cet exemple :
- `Animal` est la classe mère.
- `Chien` et `Chat` sont des classes filles qui héritent d'`Animal`.
- `Chien` et `Chat` héritent de la méthode `manger` d'`Animal`.
- `Chien` définit une méthode supplémentaire `aboyer`.
- `Chat` définit une méthode supplémentaire `miauler`.
Accéder aux attributs et méthodes de la classe mère : super()
A l'intérieur d'une classe fille, vous pouvez accéder aux attributs et aux méthodes de la classe mère en utilisant la fonction intégrée `super()`.
`super()` retourne un objet proxy qui vous permet d'appeler les méthodes de la classe mère.
Syntaxe (dans une méthode de la classe fille) :
super().methode_de_la_classe_mere(arguments)Exemple (extension du constructeur de la classe mère) :
class Animal:
def __init__(self, nom, espece):
self.nom = nom
self.espece = espece
class Chien(Animal):
def __init__(self, nom, race):
super().__init__(nom, "Canis familiaris") # Appel du constructeur de la classe mère
self.race = race # Attribut spécifique à Chien
mon_chien = Chien("Médor", "Labrador")
print(mon_chien.nom) # Affiche Médor
print(mon_chien.espece) # Affiche Canis familiaris
print(mon_chien.race) # Affiche LabradorDans cet exemple :
- La classe `Chien` hérite d'`Animal`.
- Le constructeur de `Chien` appelle le constructeur d'`Animal` en utilisant `super().__init__(nom, "Canis familiaris")`. Cela initialise les attributs `nom` et `espece` hérités d'`Animal`.
- Le constructeur de `Chien` initialise ensuite l'attribut `race`, qui est spécifique à `Chien`.
L'utilisation de `super()` est recommandée pour appeler les méthodes de la classe mère, car elle rend le code plus flexible et plus facile à maintenir (en particulier en cas d'héritage multiple, voir plus loin).
Notez que vous n'avez pas besoin d'utiliser `super()` pour accéder aux attributs ou aux méthodes de la classe mère *s'ils ne sont pas redéfinis* dans la classe fille. Python les trouvera automatiquement en remontant la hiérarchie de classes (règle LEGB).
Redéfinir des méthodes (overriding) : spécialiser le comportement
Une classe fille peut redéfinir (surcharger, "override" en anglais) une méthode de la classe mère. Cela signifie que la classe fille fournit sa propre implémentation de la méthode, qui remplace l'implémentation de la classe mère.
Pour redéfinir une méthode, il suffit de définir une méthode avec le même nom dans la classe fille.
Exemple :
class Animal:
def faire_du_bruit(self):
print("...") # Comportement générique
class Chien(Animal):
def faire_du_bruit(self):
print("Wouf wouf !") # Redéfinition de la méthode
class Chat(Animal):
def faire_du_bruit(self):
print("Miaou !") # Redéfinition de la méthode
animal = Animal()
animal.faire_du_bruit() # Affiche "..."
chien = Chien()
chien.faire_du_bruit() # Affiche "Wouf wouf !"
chat = Chat()
chat.faire_du_bruit() # Affiche "Miaou !"Dans cet exemple, les classes `Chien` et `Chat` redéfinissent la méthode `faire_du_bruit` de la classe `Animal` pour fournir un comportement spécifique à chaque type d'animal.
La redéfinition de méthodes est un mécanisme puissant qui permet de spécialiser le comportement des classes filles tout en conservant une interface commune (le nom de la méthode).
Héritage multiple (avancé)
Python supporte l'héritage multiple, ce qui signifie qu'une classe peut hériter de plusieurs classes mères.
Syntaxe :
class ClasseFille(Mere1, Mere2, Mere3):
# ...L'héritage multiple peut être utile pour combiner des fonctionnalités de plusieurs classes, mais il peut aussi rendre le code plus complexe et plus difficile à comprendre, en particulier si les classes mères ont des méthodes ou des attributs avec le même nom.
En cas de conflit de noms (par exemple, si deux classes mères définissent une méthode avec le même nom), Python utilise un ordre de résolution des méthodes (Method Resolution Order, MRO) pour déterminer quelle méthode doit être appelée. Le MRO est basé sur l'algorithme C3 linearization.
Vous pouvez obtenir le MRO d'une classe avec l'attribut spécial `__mro__` ou la méthode `mro()` :
class A:
pass
class B(A):
pass
class C:
pass
class D(B, C):
pass
print(D.__mro__) # Affiche l'ordre de recherche des méthodes
# (, , , , )
print(D.mro()) L'héritage multiple est un sujet avancé, et il est souvent recommandé de l'utiliser avec prudence. Dans de nombreux cas, il est préférable d'utiliser la composition (créer des objets qui contiennent d'autres objets) plutôt que l'héritage multiple.
Une alternative à l'héritage multiple est l'utilisation de "mixins", qui sont des classes conçues pour être héritées et qui fournissent des fonctionnalités spécifiques, sans être destinées à être utilisées seules.