1.3. L’héritage¶
Il est temps d’étendre notre exemple. Essayons à présent de modéliser les membres de nos universités. Nous nous limiterons pour cela aux professeurs et aux étudiants de bachelor et de master. Nous créerons les trois classes correspondantes pourvues chacune de quelques propriétés et d’une méthode de salutation.
class Bachelor:
def __init__(self, first, last, faculty, year, average):
self.first = first
self.last = last
self.faculty = faculty
self.year = year
self.average = average
def greet(self):
print(f"Bonjour, je suis {self.first} {self.last}.\n"
f"Je suis étudiant de bachelor en {self.faculty}.\n"
f"Ma moyenne est de {self.average}.\n"
f"Je suis actuellement en {self.year}e année.\n"
f"J'aurai terminé dans {3 - self.year} an(s) !")
class Master:
def __init__(self, first, last, faculty, year, average):
self.first = first
self.last = last
self.faculty = faculty
self.year = year
self.average = average
def greet(self):
print(f"Bonjour, je suis {self.first} {self.last}.\n"
f"Je suis étudiant de master en {self.faculty}.\n"
f"Ma moyenne est de {self.average}.\n"
f"Je suis actuellement en {self.year}e année.\n"
f"J'aurai terminé dans {2 - self.year} an(s) !")
class Professor:
def __init__(self, first, last, faculty, hours):
self.first = first
self.last = last
self.faculty = faculty
self.hours = hours
def greet(self):
print(f"Bonjour, je suis {self.first} {self.last}.\n"
f"Je suis enseignant en {self.faculty}.\n"
f"Je donne {self.hours} heures de cours par semaine.")
student1 = Bachelor("Alice", "Martin", "chimie", 2, 4.3)
student1.greet()
print()
student2 = Master("Benjamin", "Legrand", "droit", 1, 5.0)
student2.greet()
print()
professor1 = Professor("Brian", "Smith", "informatique", 15)
professor1.greet()
Bonjour, je suis Alice Martin.
Je suis étudiant de bachelor en chimie.
Ma moyenne est de 4.3.
Je suis actuellement en 2e année.
J'aurai terminé dans 1 an(s) !
Bonjour, je suis Benjamin Legrand.
Je suis étudiant de master en droit.
Ma moyenne est de 5.0.
Je suis actuellement en 1e année.
J'aurai terminé dans 1 an(s) !
Bonjour, je suis Brian Smith.
Je suis enseignant en informatique.
Je donne 15 heures de cours par semaine.
Cette solution n’est cependant pas satisfaisante, car le code est très redondant. En effet, nos trois classes possèdent de nombreux points communs :
un prénom
un nom
une faculté
une méthode de présentation
Et les deux classes d’étudiants ont encore plus de recoupements :
une année
une moyenne générale
Note
Un programme bien écrit devrait comporter le moins de redondance possible, comme le veux la maxime du bon développeur de logiciel « keep your code DRY! », DRY étant l’acronyme de « don’t repeat yourself » (ne vous répétez pas). À l’inverse, un mauvais code est WET, pour « wrote everything twice », ou « wasted everyone’s time » dans sa version la plus cynique.
Il faut alors réfléchir à un niveau d’abstraction supplémentaire et à ce que ces classes représentent. Toutes ont un nom, un prénom et sont capables de saluer, car toutes représentent un être humain. Si toutes possèdent une faculté, c’est parce que ces êtres humains sont des universitaires. Et si les étudiants en bachelor et en master ont des points communs, c’est parce que tous sont des étudiants. Ainsi, les étudiants de bachelor et des master sont des étudiants, qui avec les professeurs sont aussi des universitaires, eux-mêmes des êtres humains.
Nous obtenons de cette façon une hiérarchie de spécialisations où un concept général et parfois abstrait (être humain), se divise en concepts de plus en plus précis d’universitaire (Academic
) et d’étudiant (Student
), jusqu’à être spécialisé en nos trois classes concrètes Professor
, Bachelor
et Master
, en acquérant des attributs et des méthodes supplémentaires à chaque étape.
Cette hiérarchisation des classes se nomme l’héritage et est illustrée dans la figure 1.1 ci-dessous.

Fig. 1.1 Classes.¶
Commençons par créer une classe Human
avec les attributs propres à tous les
êtres humains.
class Human:
def __init__(self, first, last):
self.first = first
self.last = last
def greet(self):
print(f"Bonjour, je suis {self.first} {self.last}.")
human = Human("Alice", "Martin")
human.greet()
Bonjour, je suis Alice Martin.
Nous créons ensuite, une sous-classe Academic
qui hérite de la classe Human
.
Cette sous-classe est dite la classe fille (enfant, « child » en anglais) et Human
est sa super-classe ou classe mère (parent).
Pour établir cette relation d’héritage, nous indiquons le nom de la classe mère entre parenthèses au moment de la déclaration de la classe fille : class Child(Parent)
.
Il faut ensuite initialiser la classe mère en faisant appel à son constructeur depuis celui de la classe fille.
Puisque la salutation héritée ne convient pas pour la classe fille, nous la redéfinissons selon nos besoins.
Lors de l’appel de la méthode, la version faisant foi est celle qui est la plus spécialisée dans la chaîne d’héritage de la classe.
En d’autres termes, lorsque l’on appellera la méthode greet()
sur un objet de la type Academic
, c’est la version redéfinie qui sera appelée et non pas la version de la classe mère.
Si par contre la méthode n’est pas redéfinie, la version héritée de la classe mère fait foi et est bien appelée.
class Human:
def __init__(self, first, last):
self.first = first
self.last = last
def greet(self):
print(f"Bonjour, je suis {self.first} {self.last}.")
class Academic(Human): # `Academic` hérite de `Human`
def __init__(self, first, last, faculty):
# on appelle le constructeur de `Human` en lui passant l'instance de la classe
# `Academic` et les arguments pour initialiser les attributs de la classe mère
Human.__init__(self, first, last)
# puis on initialise l'attribut propre à la classe fille
self.faculty = faculty
# enfin, on redéfinit la méthode greet() pour l'adapter à la classe fille
def greet(self):
print(f"Bonjour, je suis {self.first} {self.last}.\n"
f"Je suis universitaire en {self.faculty}.")
human = Human("Alice", "Martin")
human.greet()
print()
academic = Academic("Camille", "Dubois", "histoire")
academic.greet()
Bonjour, je suis Alice Martin.
Bonjour, je suis Camille Dubois.
Je suis universitaire en histoire.
La classe Academic
hérite ainsi de tous les attributs et de toutes les méthodes de la classe Human
.
Si nous n’avions pas redéfini la méthode greet()
, elle aurait été strictement identique à celle définie dans Human
.
La version redéfinie peut cependant déléguer une partie de ses tâche à la classe mère en appelant la méthode héritée
Il s’agit d’une pratique courante que nous allons illustrer dans notre mise en œuvre de greet()
, qui demandera à la classe mère d’afficher ses attributs avant d’afficher ceux de la classe fille.
Complétons à présent la hiérarchie.
class Human:
def __init__(self, first, last):
self.first = first
self.last = last
def greet(self):
print(f"Bonjour, je suis {self.first} {self.last}.")
class Academic(Human):
def __init__(self, first, last, faculty):
Human.__init__(self, first, last)
self.faculty = faculty
def greet(self):
Human.greet(self)
print(f"Je suis universitaire en {self.faculty}.")
class Professor(Academic):
def __init__(self, first, last, faculty, hours):
Academic.__init__(self, first, last, faculty)
self.hours = hours
def greet(self):
Academic.greet(self)
print(f"J'enseigne {self.hours} heures par semaine.")
class Student(Academic):
def __init__(self, first, last, faculty, year, average):
Academic.__init__(self, first, last, faculty)
self.year = year
self.average = average
def greet(self):
Academic.greet(self)
print(f"Je suis en {self.year}e année d'études.\n"
f"Ma moyenne est de {self.average}.")
class Bachelor(Student):
def __init__(self, first, last, faculty, year, average):
Student.__init__(self, first, last, faculty, year, average)
def greet(self):
Student.greet(self)
print(f"Je termine mon bachelor dans {3 - self.year} an(s) !")
class Master(Student):
def __init__(self, first, last, faculty, year, average):
Student.__init__(self, first, last, faculty, year, average)
def greet(self):
Student.greet(self)
print(f"Je termine mon master dans {2 - self.year} an(s) !")
human = Human("Alice", "Martin")
human.greet()
print()
academic = Academic("Camille", "Dubois", "histoire")
academic.greet()
print()
professeur = Professor("Brian", "Smith", "informatique", 7)
professeur.greet()
print()
student = Student("Benjamin", "Legrand", "droit", 3, 5.1)
student.greet()
print()
bachelor = Bachelor("Emma", "Dupont", "biologie", 2, 5.3)
bachelor.greet()
print()
master = Master("David", "Petit", "informatique", 1, 4.8)
master.greet()
Bonjour, je suis Alice Martin.
Bonjour, je suis Camille Dubois.
Je suis universitaire en histoire.
Bonjour, je suis Brian Smith.
Je suis enseignant en informatique.
Je donne 7 heures de cours par semaine.
Bonjour, je suis Benjamin Legrand.
Je suis étudiant de bachelor en droit.
Ma moyenne est de 5.1.
Je suis actuellement en 3e année.
Bonjour, je suis Emma Dupont.
Je suis étudiant de bachelor en biologie.
Ma moyenne est de 5.3.
Je suis actuellement en 2e année.
J'aurai terminé dans 1 an(s) !
Bonjour, je suis David Petit.
Je suis étudiant de master en informatique.
Ma moyenne est de 4.8.
Je suis actuellement en 1e année.
J'aurai terminé dans 1 an(s) !
Le programme obtenu est plus long que dans sa première version, mais bien mieux organisé.
L’avantage de cette hiérarchisation est que, si l’on souhaite par exemple ajouter la propriété age
et la méthode joke()
à tous nos êtres humains, il suffit de les ajouter à la classe Human
et tous en hériteront automatiquement !
Et si l’on souhaite que tous nos étudiants possèdent un attribut number
pour leur matricule, il suffit de l’ajouter à la classe Student
pour que ses classes filles en héritent (mais pas la classe mère, évidemment).