Overview

  • Découvrir l’héritage dans la programmation orientée objet et les différentes formes d’héritage
  • Comprendre la méthode Overriding et la fonction super() dans le monde de la programmation orientée objet

Introduction

L’héritage est l’un des aspects les plus importants de la programmation orientée objet (POO). La clé pour comprendre l’héritage est qu’il permet la réutilisation du code. Au lieu d’écrire le même code, encore et encore, nous pouvons simplement hériter des propriétés d’une classe dans l’autre.

Ceci, comme vous pouvez l’imaginer, permet de gagner une tonne de temps. Et le temps, c’est de l’argent en science des données !

L'héritage dans la programmation orientée objet

La POO concerne les objets du monde réel et l’héritage est une façon de représenter les relations du monde réel. Voici un exemple – voiture, bus, vélo – tous ces objets font partie d’une catégorie plus large appelée Véhicule. Cela signifie qu’ils ont hérité des propriétés de la classe véhicules c’est-à-dire que tous sont utilisés pour le transport.

Nous pouvons représenter cette relation dans le code avec l’aide de l’héritage.

Une autre chose intrigante à propos de l’héritage est qu’il est transitif par nature. Mais qu’est-ce que cela signifie ? Nous le verrons en détail plus loin dans cet article. Python supporte également différents types d’héritage que je couvrirai en détail dans cet article.

Cet article est le deuxième de la série d’articles relatifs à la programmation orientée objet. Veuillez également parcourir le premier article :

  • Concepts de base de la programmation orientée objet.

Table des matières

  1. Qu’est-ce que l’héritage dans la programmation orientée objet ?
  2. Différentes formes d’héritage en programmation orientée objet
    • Héritage simple
    • Héritage multiple
    • Multi-level Inheritance
    • Hierarchical Inheritance
    • Hybrid Inheritance
  3. Method Overriding
  4. The super() function

Qu’est-ce que l’héritage dans la programmation orientée objet ?

L’héritage est la procédure par laquelle une classe hérite des attributs et des méthodes d’une autre classe. La classe dont les propriétés et les méthodes sont héritées est connue comme la classe Parent. Et la classe qui hérite des propriétés de la classe parent est la classe enfant.

La chose intéressante est que, parallèlement aux propriétés et méthodes héritées, une classe enfant peut avoir ses propres propriétés et méthodes.

Vous pouvez utiliser la syntaxe suivante:\ pour mettre en œuvre l’héritage en Python:

class parent_class:body of parent classclass child_class( parent_class):body of child class

Voyons la mise en œuvre :

class Car: #parent class def __init__(self, name, mileage): self.name = name self.mileage = mileage def description(self): return f"The {self.name} car gives the mileage of {self.mileage}km/l"class BMW(Car): #child class passclass Audi(Car): #child class def audi_desc(self): return "This is the description method of class Audi."
obj1 = BMW("BMW 7-series",39.53)print(obj1.description())obj2 = Audi("Audi A8 L",14)print(obj2.description())print(obj2.audi_desc())

Sortie :

L'héritage dans la programmation orientée objet

Nous avons créé deux classes enfant à savoir « BMW » et « Audi » qui ont hérité des méthodes et des propriétés de la classe parent « Car ». Nous n’avons fourni aucune caractéristique et méthode supplémentaire dans la classe BMW. Alors qu’il y a une méthode supplémentaire à l’intérieur de la classe Audi.

Notez comment la méthode d’instance description() de la classe parent est accessible par les objets des classes enfants à l’aide de obj1.description() et obj2.description(). Et la méthode distincte de la classe Audi est également accessible à l’aide d’obj2.audi_desc().

Nous pouvons vérifier la classe de base ou la classe parente de toute classe à l’aide d’un attribut de classe intégré __bases__

print(BMW.__bases__, Audi.__bases__)
Comme nous pouvons le voir ici, la classe de base des deux sous-classes est Car. Maintenant, voyons ce qui se passe lorsqu’on utilise __base__ avec la classe mère Car:
print( Car.__bases__ )
Héritage dans la programmation orientée objet - sous-classe Print
Chaque fois que nous créons une nouvelle classe dans Python 3.x, elle est héritée d’une classe de base intégrée appelée Object. En d’autres termes, la classe Object est la racine de toutes les classes.

Formes d’héritage dans la programmation orientée objet

Il existe globalement cinq formes d’héritage basées sur l’implication des classes parent et enfant.

1. Héritage simple

C’est une forme d’héritage dans laquelle une classe n’hérite que d’une seule classe parent. C’est la forme simple d’héritage et donc également appelée héritage simple.

class Parent: def f1(self): print("Function of parent class.")class Child(Parent): def f2(self): print("Function of child class.")object1 = Child()object1.f1()object1.f2()
Héritage dans la programmation orientée objet - Héritage simple

Ici, la classe Enfant n’hérite que d’une seule classe Parent, donc c’est un exemple d’héritage simple.

2. héritage multiple

Un héritage devient des héritages multiples lorsqu’une classe hérite de plus d’une classe parent. La classe enfant après avoir hérité des propriétés de diverses classes parentales a accès à tous leurs objets.

class Parent_1: def f1(self): print("Function of parent_1 class.")class Parent_2: def f2(self): print("Function of parent_2 class.")class Parent_3: def f3(self): print("function of parent_3 class.")class Child(Parent_1, Parent_2, Parent_3): def f4(self): print("Function of child class.")object_1 = Child()object_1.f1()object_1.f2()object_1.f3()object_1.f4()
Héritage dans la programmation orientée objet - Héritage multiple

Ici nous avons une classe Enfant qui hérite des propriétés de trois classes parentales Parent_1, Parent_2, et Parent_3. Toutes les classes ont des fonctions différentes et toutes les fonctions sont appelées à l’aide de l’objet de la classe Enfant.

Mais supposons qu’une classe Enfant hérite de deux classes ayant la même fonction :
class Parent_1: def f1(self): print("Function of parent_1 class.")class Parent_2: def f1(self): print("Function of parent_2 class.")class Child(Parent_1, Parent_2): def f2(self): print("Function of child class.")

Here, les classes Parent_1 et Parent_2 ont la même fonction f1(). Maintenant, lorsque l’objet de la classe Enfant appelle f1(), puisque la classe Enfant hérite des deux classes parentales, que pensez-vous qu’il devrait se passer ?

obj = Child() obj.f1()
L'héritage dans la programmation orientée objet

Mais pourquoi la fonction f1() de la classe Parent_2 n’a pas été héritée ?

Dans l’héritage multiple, la classe enfant cherche d’abord la méthode dans sa propre classe. Si elle ne la trouve pas, elle cherche alors dans les classes parentes profondeur_première et ordre gauche-droite. Comme il s’agissait d’un exemple facile avec seulement deux classes parentales, nous pouvons clairement voir que la classe Parent_1 a été héritée en premier, donc la classe enfant cherchera la méthode dans la classe Parent_1 avant de chercher dans la classe Parent_2.

Mais pour les problèmes d’héritage compliqués, il devient difficile d’identifier l’ordre. Donc, la façon actuelle de le faire est appelée ordre de résolution des méthodes (MRO) en Python. Nous pouvons trouver le MRO de n’importe quelle classe en utilisant l’attribut __mro__

Child.__mro__
Héritage dans la programmation orientée objet - MRO

Cela indique que la classe Child a d’abord visité la classe Parent_1 et ensuite Parent_2, donc la méthode f1() de Parent_1 sera appelée.

Prenons un exemple un peu compliqué en Python:

class Parent_1:passclass Parent_2:passclass Parent_3:passclass Child_1(Parent_1,Parent_2):passclass Child_2(Parent_2,Parent_3):passclass Child_3(Child_1,Child_2,Parent_3):pass

Ici, la classe Child_1 hérite de deux classes – Parent_1 et Parent_2. La classe Child_2 hérite également de deux classes – Parent_2 et Parent_3. Une autre classe Enfant_3 hérite de trois classes – Enfant_1, Enfant_2 et Parent_3.

Maintenant, juste en regardant cet héritage, il est assez difficile de déterminer l’ordre de résolution des méthodes pour la classe Enfant_3. Voici donc l’utilisation réelle de __mro__-

Child_3.__mro__

Nous pouvons voir que d’abord, l’interprète recherche Child_3, puis Child_1 suivi de Parent_1, Child_2, Parent_2 et Parent_3 respectivement.

3. Héritage multi-niveaux

Par exemple, une classe_1 est héritée par une classe_2 et cette classe_2 est également héritée par la classe_3 et ce processus se poursuit. C’est ce qu’on appelle l’héritage à plusieurs niveaux. Comprenons avec un exemple:

class Parent: def f1(self): print("Function of parent class.")class Child_1(Parent): def f2(self): print("Function of child_1 class.")class Child_2(Child_1): def f3(self): print("Function of child_2 class.")obj_1 = Child_1()obj_2 = Child_2()obj_1.f1()obj_1.f2()print("\n")obj_2.f1()obj_2.f2()obj_2.f3()

Ici, la classe Child_1 hérite de la classe Parent et la classe Child_2 hérite de la classe Child_1. Dans cette Child_1 a accès aux fonctions f1() et f2() alors que Child_2 a accès aux fonctions f1(), f2() et f3(). Si nous allons essayer d’accéder à la fonction f3() en utilisant l’objet de la classe Class_1 alors une erreur se produira en déclarant :

L’objet ‘Enfant_1’ n’a pas d’attribut ‘f3’

obj_1.f3()

4- Héritage hiérarchique

Dans celui-ci, différentes classes Enfants héritent d’une seule classe Parent. L’exemple donné dans l’introduction de l’héritage est un exemple d’héritage hiérarchique puisque les classes BMW et Audi héritent de la classe Car.

Pour simplifier, regardons un autre exemple :

class Parent:deff1(self):print("Function of parent class.")class Child_1(Parent):deff2(self):print("Function of child_1 class.")class Child_2(Parent):deff3(self):print("Function of child_2 class.")obj_1 = Child_1()obj_2 = Child_2()obj_1.f1()obj_1.f2()print('\n')obj_2.f1()obj_2.f3()

Ici, deux classes enfants héritent de la même classe Parent. La classe Enfant_1 a accès aux fonctions f1() de la classe Parent et à la fonction f2() d’elle-même. Alors que la classe Enfant_2 a accès aux fonctions f1() de la classe Parent et à la fonction f3() d’elle-même.

5- Héritage hybride

Lorsqu’il y a une combinaison de plus d’une forme d’héritage, on parle d’héritage hybride. Ce sera plus clair après cet exemple :

class Parent: def f1(self): print("Function of parent class.")class Child_1(Parent): def f2(self): print("Function of child_1 class.")class Child_2(Parent): def f3(self): print("Function of child_2 class.")class Child_3(Child_1, Child_2): def f4(self): print("Function of child_3 class.")obj = Child_3()obj.f1()obj.f2()obj.f3()obj.f4()

Dans cet exemple, deux classes ‘Child_1′ et ‘Child_2’ sont dérivées de la classe de base ‘Parent’ en utilisant l’héritage hiérarchique. Une autre classe ‘Child_3’ est dérivée des classes ‘Child_1’ et ‘Child_2’ en utilisant l’héritage multiple. La classe ‘Enfant_3’ est maintenant dérivée en utilisant l’héritage hybride.

Méthode Overriding

Le concept de overriding est très important dans l’héritage. Il donne la capacité spéciale aux classes/sous-classes enfant de fournir une implémentation spécifique à une méthode qui est déjà présente dans leurs classes parentales.

class Parent: def f1(self): print("Function of Parent class.")class Child(Parent): def f1(self): print("Function of Child class.")obj = Child()obj.f1()

Ici, la fonction f1() de la classe Enfant a surchargé la fonction f1() de la classe Parent. Chaque fois que l’objet de la classe Enfant invoquera f1(), la fonction de la classe Enfant sera exécutée. Cependant, l’objet de la classe Parent peut invoquer la fonction f1() de la classe Parent.

obj_2 = Parent()obj_2.f1()

La fonction super()

La fonction super() en Python renvoie un objet proxy qui référence la classe parent en utilisant le mot-clé super. Ce mot-clé super() est essentiellement utile pour accéder aux méthodes surchargées de la classe parente.

La documentation officielle de la fonction super() situe deux utilisations principales de super() :
  1. Dans une hiérarchie de classes avec héritage simple, super aide à faire référence aux classes parentes sans les nommer explicitement, rendant ainsi le code plus maintenable.

    Par exemple-

    class Parent: def f1(self): print("Function of Parent class.")class Child(Parent): def f1(self): super().f1() print("Function of Child class.")obj = Child()obj.f1()

    Ici, à l’aide de super().f1(), la méthode f1() de la super classe de la classe Child i.e classe Parent a été appelée sans la nommer explicitement.

    Une chose à noter ici est que la classe super() peut accepter deux paramètres- le premier est le nom de la sous-classe et le second est un objet qui est une instance de cette sous-classe. Voyons comment-
    class Parent: def f1(self): print("Function of Parent class.")class Child(Parent): def f1(self): super( Child, self ).f1() print("Function of Child class.")obj = Child()obj.f1()

    Le premier paramètre fait référence à la sous-classe Child, tandis que le second paramètre fait référence à l’objet de Child qui, dans ce cas, est self. Vous pouvez voir que le résultat après avoir utilisé super() et super( Child, self) est le même car, dans Python 3, super( Child, self) est équivalent à self().

    Voyons maintenant un autre exemple utilisant la fonction __init__.
    class Parent(object): def__init__(self, ParentName): print(ParentName, 'is derived from another class.')class Child(Parent): def__init__(self, ChildName): print(name,'is a sub-class.') super().__init__(ChildName)obj = Child('Child')

    Ce que nous avons fait ici, c’est que nous avons appelé la fonction __init__ de la classe Parent (à l’intérieur de la classe Child) en utilisant super().__init__( ChildName ). Et comme la méthode __init__ de la classe Parent requiert un argument, celui-ci a été passé sous la forme « ChildName ». Donc après avoir créé l’objet de la classe Enfant, d’abord la fonction __init__ de la classe Enfant a été exécutée, et après cela la fonction __init__ de la classe Parent.

  2. Le deuxième cas d’utilisation est de supporter les héritages multiples coopératifs dans un environnement d’exécution dynamique.
    class First(): def __init__(self): print("first") super().__init__()class Second(): def __init__(self): print("second") super().__init__()class Third(Second, First): def __init__(self): print("third") super().__init__()obj = Third()

    L’appel super() trouve la méthode suivante dans le MRO à chaque étape, c’est pourquoi First et Second doivent l’avoir aussi, sinon l’exécution s’arrête à la fin de first().__init__.

    Notez que la super-classe de First et Second est Object.

    Trouvons aussi le MRO de Third().

    Third.__mro__

    L’ordre est Third > Second > First et le même est l’ordre de notre sortie.

Notes de fin

Pour conclure, dans cet article, j’ai avancé le concept d’héritage dans la programmation orientée objet en Python. J’ai couvert différentes formes d’Héritage et certains des concepts communs de l’Héritage tels que la méthode Overriding et la fonction super().

J’espère que vous avez compris les concepts expliqués dans cet article. Faites-moi savoir dans les commentaires ci-dessous si vous avez des questions.

Vous pouvez également lire cet article sur notre APP mobile.

Catégories : Articles

0 commentaire

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *