Bonjour ! Dans la leçon précédente, vous avez appris à créer vos propres objets en Python en définissant des classes. Vous avez vu comment une classe, comme notre classe Employee, sert de plan pour regrouper des données (attributs) et des comportements (méthodes).
Aujourd'hui, nous allons approfondir ce concept avec l'une des idées les plus puissantes de la programmation orientée objet : l'héritage. L'objectif de cette leçon est d'expliquer le concept d'héritage en créant une classe enfant. Vous découvrirez comment une nouvelle classe peut "hériter" de toutes les fonctionnalités d'une classe existante, vous permettant de réutiliser du code et de construire des structures logiques et hiérarchiques. C'est le mécanisme exact que nous utiliserons bientôt pour construire des réseaux de neurones dans PyTorch.
1. Qu'est-ce que l'héritage ?
L'héritage est un moyen de former de nouvelles classes en utilisant des classes qui ont déjà été définies. Les nouvelles classes, appelées classes enfants (ou subclasses), héritent de tous les attributs et méthodes des classes dont elles héritent, appelées classes parentes (ou superclasses).
L'idée principale est de modéliser une relation de type "est un" (is-a). Par exemple :
- Une
Voitureest unVéhicule. - Un
Développeurest unEmployé. - Un
Chienest unAnimal.
Dans ces exemples, le Véhicule, l'Employé et l'Animal sont des classes parentes, générales. La Voiture, le Développeur et le Chien sont des classes enfants, plus spécifiques, qui partagent les caractéristiques de leur parent mais y ajoutent aussi leurs propres spécificités.

La vidéo suivante, en arabe, donne une excellente introduction conceptuelle à l'héritage avec des analogies du monde réel.
Cette vidéo de "Two Pi" explique le concept d'héritage en utilisant une analogie facile à comprendre.
Regardez la première partie de la vidéo (jusqu'à la fin de l'explication conceptuelle). Elle explique comment l'héritage en programmation est similaire à l'héritage dans la vie réelle et introduit les termes de classe parente (superclass) et classe enfant (subclass) à l'aide d'un exemple Animal.
2. Créer une classe enfant (Subclass)
En Python, la création d'une classe enfant est très simple. On indique la classe parente entre parenthèses après le nom de la classe enfant. Reprenons la classe Employee de la leçon précédente.
# Classe Parente (Superclass)
class Employee:
raise_amount = 1.04 # 4% d'augmentation
def __init__(self, first, last, pay):
self.first = first
self.last = last
self.pay = pay
self.email = first.lower() + '.' + last.lower() + '@company.com'
def full_name(self):
return f'{self.first} {self.last}'
def apply_raise(self):
self.pay = int(self.pay * self.raise_amount)
Maintenant, imaginons que nous voulons créer une classe plus spécifique pour les Developer. Un développeur est un employé, donc il devrait hériter de la classe Employee.
# Classe Enfant (Subclass)
class Developer(Employee):
pass # 'pass' signifie que nous n'ajoutons rien pour l'instant
Même avec ce code minimal, la classe Developer hérite de tout ce qui se trouve dans Employee. Une instance de Developer aura un nom, un salaire, une méthode full_name(), etc., sans que nous ayons à réécrire une seule ligne de ce code.
La vidéo suivante de Corey Schafer, qui fait suite à celle de la leçon précédente, démontre ce processus de manière très claire.
Python OOP Tutorial 4: Inheritance - Creating Subclasses
Cette vidéo continue l'exemple de la classe Employee pour introduire l'héritage.
Regardez le début de la vidéo où il explique comment créer la sous-classe Developer. Il montre qu'en passant simplement Employee entre parenthèses, la nouvelle classe Developer peut être utilisée exactement comme la classe Employee.

3. Personnaliser la classe enfant
L'intérêt de l'héritage n'est pas seulement de copier des fonctionnalités, mais de pouvoir les étendre et les modifier. Une classe enfant peut :
- Modifier les attributs ou méthodes hérités (on parle de surcharger, ou override).
- Ajouter de nouveaux attributs et méthodes.
Surcharger des attributs
Supposons que les développeurs aient une augmentation de salaire plus importante que les autres employés, disons 10% au lieu de 4%. Nous pouvons simplement redéfinir la variable raise_amount dans la classe Developer.
class Developer(Employee):
raise_amount = 1.10 # On surcharge la valeur de la classe parente
Lorsque nous appellerons apply_raise() sur une instance de Developer, Python utilisera cette nouvelle valeur. Il cherche toujours l'attribut ou la méthode dans la classe enfant d'abord, avant de remonter la "chaîne d'héritage" vers la classe parente.
Python OOP Tutorial 4: Inheritance - Creating Subclasses
Continuons avec la vidéo de Corey Schafer.
Regardez comment il utilise la fonction help() pour visualiser la chaîne d'héritage (Method Resolution Order). Ensuite, voyez comment il surcharge l'attribut raise_amount dans la classe Developer et montre que cela n'affecte pas la classe Employee.
Ajouter de nouveaux attributs et la fonction super()
Et si nous voulions que chaque Developer ait un attribut supplémentaire, comme son langage de programmation principal ? La méthode __init__ de la classe Employee ne sait pas comment gérer ce nouvel attribut. Nous devons donc définir une nouvelle méthode __init__ pour notre classe Developer.
Cependant, nous ne voulons pas réécrire toute la logique pour first, last, et pay. Nous voulons dire à Python : "Pour les attributs que la classe parente sait gérer, laisse-la faire son travail, et moi je m'occupe du reste." C'est là qu'intervient la fonction super().
super() nous donne accès aux méthodes de la classe parente. En appelant super().__init__(...), nous pouvons exécuter le constructeur de la classe Employee depuis celui de la classe Developer.
Cet article de GeeksforGeeks donne un excellent aperçu de l'héritage et de la fonction super().
Commencez par lire l'introduction qui montre un exemple simple d'héritage avec les classes Animal et Dog. Ensuite, lisez la section sur la fonction super(). Elle explique clairement comment et pourquoi utiliser super().__init__() pour initialiser les attributs hérités sans dupliquer le code.
Voici à quoi ressemblerait notre classe Developer mise à jour :
class Developer(Employee):
raise_amount = 1.10
def __init__(self, first, last, pay, prog_lang):
# Laisse la classe parente gérer first, last, et pay
super().__init__(first, last, pay)
# Gère le nouvel attribut spécifique au Developer
self.prog_lang = prog_lang
dev_1 = Developer('Hussam', 'Hourani', 70000, 'Python')
print(dev_1.email) # Hérité de Employee
print(dev_1.prog_lang) # Spécifique à Developer
Le visionnage de la partie suivante de la vidéo de Corey Schafer vous montrera ce processus en action.
Python OOP Tutorial 4: Inheritance - Creating Subclasses
Cette partie de la vidéo est cruciale pour comprendre l'utilisation de super().
Regardez attentivement comment il ajoute le langage de programmation comme nouvel attribut. Il explique pourquoi il ne faut pas copier-coller le code de __init__ et comment utiliser super().__init__(...) pour passer les arguments à la classe parente.
4. Le lien avec PyTorch
Pourquoi tout cela est-il si important pour vos objectifs en deep learning ? La réponse est simple : la création de modèles dans PyTorch repose entièrement sur l'héritage.
Lorsque vous construirez un réseau de neurones, vous créerez une classe qui hérite de la classe de base de PyTorch, torch.nn.Module.
import torch
import torch.nn as nn
class MonPremierReseau(nn.Module): # Héritage !
def __init__(self):
super().__init__() # Crucial pour initialiser la classe parente !
# Ici, vous définirez les couches de votre réseau (attributs)
self.couche1 = nn.Linear(in_features=10, out_features=5)
self.couche2 = nn.Linear(in_features=5, out_features=1)
def forward(self, x):
# Ici, vous définirez comment les données passent à travers les couches (méthode surchargée)
x = self.couche1(x)
x = self.couche2(x)
return x
En héritant de nn.Module, votre classe obtient gratuitement une tonne de fonctionnalités essentielles : le suivi des paramètres du modèle, la possibilité de le déplacer sur un GPU, et bien plus encore. Vous n'avez qu'à vous concentrer sur la personnalisation :
- Définir vos couches dans
__init__. - Définir le flux de données en surchargeant la méthode
forward.
Comprendre l'héritage aujourd'hui vous donne la clé pour comprendre l'architecture de n'importe quel modèle PyTorch que vous rencontrerez.
Conclusion
Dans cette leçon, vous avez découvert le concept d'héritage, un pilier de la programmation orientée objet qui favorise la réutilisation et l'organisation logique du code.
Points clés à retenir :
- L'héritage permet à une classe enfant (subclass) de recevoir les attributs et méthodes d'une classe parente (superclass).
- Il modélise une relation "est un" (
Developerest unEmployee). - La syntaxe en Python est
class Enfant(Parent):. - Une classe enfant peut surcharger (modifier) des attributs et des méthodes, ou en ajouter de nouveaux.
- La fonction
super().__init__()est essentielle pour appeler le constructeur de la classe parente depuis la classe enfant, évitant ainsi la duplication de code.
Maintenant que vous maîtrisez les fondements de la POO en Python, nous sommes prêts à aborder la bibliothèque qui est au cœur de votre objectif : PyTorch. Dans la prochaine leçon, nous commencerons par manipuler l'objet fondamental de PyTorch, le tenseur. Vous apprendrez à créer et à effectuer des opérations sur ces tableaux multi-dimensionnels qui sont la base de toutes les données et calculs en deep learning.