Programmation Orientée Objet en Python
  • Back to Main Website
  • Home
  • Introduction: Histoire et Concepts
    • Introduction: Histoire et Concepts
    • Histoire de la programmation
    • Première Structuration des données
    • Naissance de la POO
    • Python: tout n’est qu’objet
    • Python : Simplicité des objets et performance sous-jacente
    • Classes en Python : Concepts fondamentaux

    • Travaux Pratiques
    • Construire sa propre Liste
    • Construire son propre DataFrame
  • Encapsulation, Héritage, Composition et Dunder
    • Encapsulation, Heritage, Composition et Dunder
    • Encapsulation en Python
    • Héritage en Python : Concept et intérêt
    • Héritage vs Composition
    • Méthodes Dunder en Python
    • Python call Method: A Fun Exploration

    • Travaux Pratiques
    • TP: Heritage avec le pricing d’option
    • TP : Ajouter des méthodes dunder à DataFrameSimple
    • TP : Étendre la classe Liste avec des méthodes dunder
    • TP: Dunder Method with Tensor for Automatic Differentiation
  • Polymorphisme et Surcharge
    • Polymorphisme et Surcharge
    • Polymorphism in Object-Oriented Programming
    • Polymorphism in Python: Function Overloading and Type Checking
    • Class Creation: Standard vs type()
    • Type Hinting, Typing Module, and Linters in Python
    • Abstract Classes
    • Protocol Classes

    • Travaux Pratiques
    • TP
  • Decorators
    • Design Patterns
    • The decorator pattern
    • Decorator Practically
    • Built-in Decorators and Standard Library Decorators in Python
    • Practical Decorators in Python Libraries

    • Travaux Pratiques
    • TP: Monte Carlo Option Pricing with Decorators
    • TP: Optimizing Heston Model Monte Carlo Simulation
  • Project Management and Packaging
    • Project and Package
    • Organizing Python Projects
    • Understanding imports
    • Python Package Management and Virtual Environments
    • Unit Testing in Python

    • Travaux Pratiques
    • TP: Creating a Linear Regression Package
  • Design Patterns
    • OOP Design Patterns
    • Python-Specific Design Patterns
    • Creation Design Patterns
    • Structural Design Patterns
    • Behavioral Design Pattern

    • Travaux Pratiques
    • TP
  • Sujets de Projets possibles
    • Projets
    • Projets POO - 2024-2025
  • Code source
  1. Héritage en Python : Concept et intérêt
  • Encapsulation, Heritage, Composition et Dunder
  • Encapsulation en Python
  • Héritage en Python : Concept et intérêt
  • Héritage vs Composition
  • Méthodes Dunder en Python
  • Python call Method: A Fun Exploration
  • Travaux Pratiques
    • TP: Heritage avec le pricing d’option
    • TP : Ajouter des méthodes dunder à DataFrameSimple
    • TP : Étendre la classe Liste avec des méthodes dunder
    • TP: Dunder Method with Tensor for Automatic Differentiation

On this page

  • L’héritage en Python : Concept et intérêt
    • 1.1 Intérêt de l’héritage
    • 1.2 Héritage implicite de la classe object
    • 2. Héritage multiple en Python
      • 2.1 Syntaxe de l’héritage multiple
      • 2.2 Intérêt concret de l’héritage multiple
    • 3. Method Resolution Order (MRO) en détail
      • 3.1 Définition et importance
      • 3.2 Algorithme C3 de linéarisation
      • 3.3 Visualisation de la MRO
      • 3.4 Exemple détaillé de résolution de méthode
      • 3.5 Implications pour le design
    • 4. Utilisation de super()
      • 4.1 Définition et utilité
      • 4.2 Syntaxe de base
      • 4.3 super() avec des arguments
      • 4.4 Utilisation de super() avec *args et **kwargs
      • 4.4 super() dans l’héritage multiple
    • 5. Gestion des appels aux méthodes parentes en cas d’héritage multiple
      • 5.1 Appel direct à une méthode de classe parente
      • 5.2 Avantages et inconvénients
      • 5.3 Bonnes pratiques

Héritage en Python : Concept et intérêt

Cours
Fondamentaux
Découvrez le concept d’héritage en Python, son intérêt et son application dans la création de hiérarchies de classes. Apprenez comment Python gère l’héritage implicite de la classe object, l’héritage multiple, et la résolution de l’ordre des méthodes (MRO).
Author

Remi Genet

Published

2024-10-21

L’héritage en Python : Concept et intérêt


L’héritage est un mécanisme fondamental de la programmation orientée objet qui permet à une classe de baser sa définition sur celle d’une autre classe. Cela favorise la réutilisation du code et permet de créer des hiérarchies de classes.

1.1 Intérêt de l’héritage

  1. Réutilisation du code : L’héritage permet d’éviter la duplication de code en définissant des comportements communs dans une classe parente.

  2. Organisation logique : Il permet de créer des hiérarchies de classes qui reflètent des relations logiques entre concepts.

  3. Extensibilité : Les classes dérivées peuvent étendre ou modifier le comportement des classes parentes, rendant le code plus flexible.

  4. Polymorphisme : L’héritage est à la base du polymorphisme, permettant de traiter des objets de classes différentes de manière uniforme.

1.2 Héritage implicite de la classe object

En Python, toutes les classes héritent implicitement de la classe object, même si ce n’est pas spécifié explicitement.

L’héritage de object fournit des méthodes de base comme __init__, __str__, __repr__, etc. C’est pourquoi toutes les classes en Python ont certaines méthodes en commun.

2. Héritage multiple en Python

Python supporte l’héritage multiple, ce qui signifie qu’une classe peut hériter de plusieurs classes parentes.

2.1 Syntaxe de l’héritage multiple

2.2 Intérêt concret de l’héritage multiple

  1. Combinaison de fonctionnalités : Permet de combiner des fonctionnalités de différentes classes en une seule.

  2. Réutilisation de code provenant de sources multiples : Utile lorsqu’une classe doit hériter de comportements de plusieurs classes non liées.

  3. Implémentation de designs complexes : Permet de créer des structures de classes plus flexibles et adaptables.

Exemple concret :

Ici, Canard hérite des capacités de Nageur et Volant, combinant ainsi ces deux comportements.

3. Method Resolution Order (MRO) en détail

La MRO est cruciale pour comprendre comment Python gère l’héritage, en particulier l’héritage multiple.

3.1 Définition et importance

La MRO définit l’ordre dans lequel Python recherche les méthodes et les attributs dans une hiérarchie de classes. Elle est particulièrement importante en cas d’héritage multiple, où plusieurs classes parentes peuvent définir la même méthode.

3.2 Algorithme C3 de linéarisation

Python utilise l’algorithme C3 pour déterminer la MRO. Cet algorithme garantit que :

  1. Les sous-classes apparaissent avant les classes parentes.
  2. L’ordre de déclaration des classes parentes est respecté.
  3. La MRO est monotone (une classe apparaît toujours avant ses parents).

3.3 Visualisation de la MRO

On peut visualiser la MRO d’une classe avec la méthode mro() ou l’attribut __mro__ :

3.4 Exemple détaillé de résolution de méthode

Considérons cet exemple pour illustrer comment la MRO fonctionne :

Ici, D hérite de B et C. Lorsque d.methode() est appelé, Python suit la MRO : D -> B -> C -> A -> object. Il trouve d’abord la méthode dans B, donc c’est celle-ci qui est exécutée.

3.5 Implications pour le design

La compréhension de la MRO est cruciale pour : - Éviter les conflits de noms dans l’héritage multiple. - Comprendre l’ordre d’exécution des méthodes. - Concevoir des hiérarchies de classes cohérentes et prévisibles.

4. Utilisation de super()

La fonction super() est un outil puissant en Python pour gérer l’héritage de manière élégante et flexible.

4.1 Définition et utilité

super() permet d’appeler des méthodes de la classe parente dans une classe dérivée. Elle est particulièrement utile pour :

  1. Étendre le comportement d’une méthode parente sans la réécrire entièrement.
  2. Assurer une gestion correcte de l’héritage multiple.

4.2 Syntaxe de base

class Parent:
    def methode(self):
        print("Méthode du parent")

class Enfant(Parent):
    def methode(self):
        super().methode()  # Appelle la méthode de la classe parente
        print("Méthode de l'enfant")

enfant = Enfant()
enfant.methode()
# Affiche:
# Méthode du parent
# Méthode de l'enfant

4.3 super() avec des arguments

Dans le constructeur, il est courant d’utiliser super() pour initialiser la classe parente :

class Parent:
    def __init__(self, nom):
        self.nom = nom

class Enfant(Parent):
    def __init__(self, nom, age):
        super().__init__(nom)  # Appelle le constructeur du parent
        self.age = age

enfant = Enfant("Alice", 10)
print(enfant.nom, enfant.age)  # Affiche: Alice 10

4.4 Utilisation de super() avec *args et **kwargs

L’utilisation de *args et **kwargs avec super() permet de créer des classes plus flexibles, capables de transmettre des arguments variables à leurs classes parentes.

class Parent:
    def __init__(self, *args, **kwargs):
        print("Arguments positionnels:", args)
        print("Arguments nommés:", kwargs)

class Enfant(Parent):
    def __init__(self, *args, **kwargs):
        print("Initialisation de Enfant")
        super().__init__(*args, **kwargs)

# Utilisation
enfant = Enfant(1, 2, 3, nom="Alice", age=10)

Cette approche est particulièrement utile lorsque : - Vous ne savez pas à l’avance combien d’arguments la classe parente pourrait avoir. - Vous voulez permettre l’ajout futur d’arguments sans casser l’héritage. - Vous travaillez avec des hiérarchies de classes complexes où différentes classes parentes peuvent avoir différents paramètres.

Pour plus de détails sur l’utilisation de *args et **kwargs, vous pouvez consulter cette ressource.

4.4 super() dans l’héritage multiple

Dans le cas de l’héritage multiple, super() suit l’ordre défini par la MRO :

class A:
    def methode(self):
        print("Méthode de A")

class B(A):
    def methode(self):
        print("Méthode de B")
        super().methode()

class C(A):
    def methode(self):
        print("Méthode de C")
        super().methode()

class D(B, C):
    def methode(self):
        print("Méthode de D")
        super().methode()

d = D()
d.methode()
# Affiche:
# Méthode de D
# Méthode de B
# Méthode de C
# Méthode de A

5. Gestion des appels aux méthodes parentes en cas d’héritage multiple

Dans certains cas, vous pourriez vouloir appeler spécifiquement la méthode d’une classe parente particulière, plutôt que de suivre la MRO.

5.1 Appel direct à une méthode de classe parente

Vous pouvez appeler directement une méthode d’une classe parente spécifique en utilisant le nom de la classe :

class A:
    def methode(self):
        print("Méthode de A")

class B:
    def methode(self):
        print("Méthode de B")

class C(A, B):
    def methode(self):
        print("Méthode de C")
        A.methode(self)  # Appel direct à la méthode de A
        B.methode(self)  # Appel direct à la méthode de B

c = C()
c.methode()
# Affiche:
# Méthode de C
# Méthode de A
# Méthode de B

5.2 Avantages et inconvénients

Avantages : - Contrôle précis sur quelle méthode parente est appelée. - Utile lorsque vous avez besoin d’un comportement spécifique d’une classe parente.

Inconvénients : - Peut rendre le code moins flexible si la structure d’héritage change. - Risque de contourner la MRO, ce qui peut mener à des comportements inattendus.

5.3 Bonnes pratiques

  1. Utilisez super() par défaut pour respecter la MRO et maintenir la flexibilité du code.
  2. N’utilisez l’appel direct que lorsque c’est absolument nécessaire et documentez clairement pourquoi.
  3. Soyez conscient des implications sur la maintenance du code à long terme.
Back to top
Encapsulation en Python
Héritage vs Composition

Programmation Orienté Object en Python, Rémi Genet.
Licence
Code source disponible sur Github

 

Site construit avec et Quarto
Inspiration pour la mise en forme du site ici
Code source disponible sur GitHub