Aller au contenu principal

Polymorphisme

Le polymorphisme est un concept fondamental en programmation orientée objet. Il permet de définir des méthodes dans une classe de base qui peuvent être redéfinies dans les classes dérivées. Cela permet à un objet d'une classe dérivée d'être traité comme un objet de la classe de base, mais avec un comportement spécifique à la classe dérivée. Ce type de polymorphisme est appelé polymorphisme d'inclusion ou de sous-typage.

Surcharge de méthodes

En Python, le polymorphisme est généralement réalisé par la surcharge de méthodes. Cela signifie que vous pouvez définir une méthode dans une classe de base, puis la redéfinir dans une classe dérivée.

Par exemple, vous pouvez définir une classe Animal avec une méthode parler, puis définir une classe Chien qui hérite de Animal et redéfinit la méthode parler pour qu'elle affiche "Le chien aboie". Voici un exemple :

class Animal:
def parler(self):
print("L'animal parle.")

class Chien(Animal):
def parler(self):
print("Le chien aboie.")

animal = Animal()
chien = Chien()

for el in [animal, chien]:
el.parler() # Affiche : L'animal parle. puis Le chien aboie.

Comme on peut le voir, la méthode parler de la classe Chien remplace celle de la classe Animal. De plus, dans ce code la liste contient des objets de type Animal et Chien, mais la méthode parler appelée est commune aux deux objets et donc le code fonctionne correctement.

Extension de méthodes

Si l'on souhaite que la méthode de la classe dérivée appelle la méthode de la classe de base, on peut utiliser le mot-clé super(). Cela permet d'appeler la méthode de la classe de base depuis la classe dérivée. Voici un exemple :

class Animal:
def parler(self):
print("L'animal parle.")

class Chien(Animal):
def parler(self):
super().parler() # Appelle la méthode de la classe de base
print("Le chien aboie.")

chien = Chien()
chien.parler() # Affiche : L'animal parle. puis Le chien aboie.

Polymorphisme et typage canard

Puisque tout est comme un dictionnaire en Python, le polymorphisme est très facile à mettre en œuvre. Lorsque vous appelez une méthode sur un objet, Python cherche d'abord la méthode dans le dictionnaire de l'objet. Si la signature de la méthode est trouvée, Python appelle la méthode. Sinon, Python remonte la chaîne d'héritage pour trouver la méthode.

Ainsi, il n'est même pas nécessaire d'hériter d'une classe mère pour avoir une méthode en commun. Vous pouvez simplement ajouter une méthode à un objet existant qui a la même signature qu'une méthode de la classe mère.

class Animal:
def parler(self):
print("L'animal parle.")

class Chien:
def parler(self):
print("Le chien aboie.")

animal = Animal()
chien = Chien()
for el in [animal, chien]:
el.parler() # La méthode parler est disponible pour les deux objets.

C'est ce qu'on appelle le typage canard en programmation orientée objet. Si un objet a la méthode parler, alors il peut être traité comme une instance d'une classe qui a la méthode parler.

Quand utiliser le typage canard ?

De façon à bien structurer votre code, il est recommandé de définir des classes mères, des classes abstraites ou des interfaces pour garantir que les méthodes sont redéfinies dans les classes filles. Cela permet de rendre votre code plus lisible et plus facile à maintenir. Cela évite aussi de dupliquer du code.

Néanmoins, l'utilisation du typage canard permet de ne pas avoir à utiliser l'héritage pour obtenir du polymorphisme. Vous pouvez simplement ajouter des méthodes à des objets existants pour obtenir le même effet. Cela est très utile lorsque vous devez interagir avec des objets qui ne sont pas sous votre contrôle, comme ceux provenant de bibliothèques tierces ou de la bibliothèque standard.

Par exemple, la fonction open de Python retourne un objet qui représente un fichier. L'objet retourné par open a plusieurs méthodes, mais si votre code n'a besoin que de la méthode read, vous pouvez simplement ajouter cette méthode à un objet existant pour obtenir le même effet.

class MonFichier:
def __init__(self, contenu):
self.contenu = contenu

def read(self):
return self.contenu


f = MonFichier("Hello, world!")
print(f.read()) # Affiche : Hello, world!

Ainsi, chaque fois qu'une fonction dans votre code a besoin d'un fichier et utilise uniquement la méthode read, vous pouvez simplement lui passer un objet MonFichier. Ceci est beaucoup plus flexible et bien plus simple que d'utiliser l'héritage.