Classe abstraite et interface
Nous avons vu qu'avec la notion d'héritage, il est possible de définir des classes qui héritent des attributs et des méthodes d'une classe mère. Il est aussi possible pour une classe fille de redéfinir une méthode de la classe mère.
Il arrive parfois que la classe mère constitue seulement un gabarit pour les classes filles. C'est-à-dire qu'on ne crée pas d'instances de cette classe. On parle alors de classe abstraite.
Un exemple de classe abstraite est la classe Animal
qui pourrait contenir des
méthodes comme manger
, dormir
, se_deplacer
, etc. Ces méthodes pourraient
être définies de manière abstraite, c'est-à-dire sans corps, car chaque animal
les implémentera de manière différente. En effet, il n'existe pas dans la nature
d'animal qui soit simplement un Animal
, mais plutôt des chiens, des chats, des
oiseaux, etc. qui sont des types d'animaux.
Dans un premier temps, on peut implémenter le concept d'une classe abstraite en
Python en utilisant le mot-clé pass
dans les méthodes abstraites. Par exemple :
class Animal:
def manger(self):
pass
def dormir(self):
pass
def se_deplacer(self):
pass
class Chien(Animal):
def manger(self):
print("Le chien mange de la viande.")
def dormir(self):
print("Le chien dort dans sa niche.")
def se_deplacer(self):
print("Le chien court dans le jardin.")
Dans cet exemple, les méthodes de la classe Animal
ne font rien. Ainsi, si un
programmeur instancie un objet de type Animal
et appelle une de ces méthodes,
il ne se passera rien car les méthodes sont vides.
Cette solution n'est pas optimale car elle ne permet pas de s'assurer que
toutes les méthodes de la classe Animal
sont redéfinies dans les classes
filles. De plus, il est possible d'instancier un objet de type Animal
, ce qui
n'a pas de sens dans notre exemple.
Les classes abstraites en Python
Pour pallier ces problèmes, Python propose un module nommé abc
(pour
Abstract Base Classes) qui permet de définir des classes abstraites.
Voici comment on pourrait réécrire notre exemple en utilisant le module abc
:
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def manger(self):
pass
@abstractmethod
def dormir(self):
pass
@abstractmethod
def se_deplacer(self):
pass
class Chien(Animal):
def manger(self):
print("Le chien mange de la viande.")
def dormir(self):
print("Le chien dort dans sa niche.")
def se_deplacer(self):
print("Le chien court dans le jardin.")
La classe Animal
hérite de la classe ABC
et on utilise le décorateur
@abstractmethod
pour indiquer que les méthodes de la classe Animal
sont
abstraites. Ainsi, si un programmeur oublie de redéfinir une méthode abstraite
dans une classe fille, Python lèvera une exception. De même, si on tente
d'instancier un objet de type Animal
, Python lèvera une exception.
a = Animal() # Lève une exception TypeError
class Chat(Animal):
def manger(self):
print("Le chat mange des croquettes.")
def dormir(self):
print("Le chat dort sur le canapé.")
c = Chat() # Lève une exception TypeError, car la méthode se_deplacer n'est pas redéfinie
Les interfaces
Ce que nous venons de voir avec la classe Animal
est un exemple d'interface.
Une interface est une classe abstraite qui ne contient que des méthodes
abstraites. Elle ne contient aucun attribut et aucune méthode concrète. Elle
définit en quelque sorte un contrat que les classes qui l'implémentent doivent
respecter. Ainsi, le programmeur peut être sûr que les classes qui implémentent
l'interface possèdent les méthodes définies dans l'interface.
Classes abstraites avec des attributs
Il est tout à fait possible de définir des attributs dans une classe abstraite et certaines méthodes concrètes. Par exemple :
from abc import ABC, abstractmethod
class Animal(ABC):
def __init__(self, nom):
self.nom = nom
@abstractmethod
def manger(self):
pass
@abstractmethod
def dormir(self):
pass
@abstractmethod
def se_deplacer(self):
pass
def __str__(self):
return f"{self.nom} est un animal."
class Chien(Animal):
def manger(self):
print("Le chien mange de la viande.")
def dormir(self):
print("Le chien dort dans sa niche.")
def se_deplacer(self):
print("Le chien court dans le jardin.")
c = Chien("Médor")
print(c.nom) # Affiche : Médor
print(c) # Affiche : Médor est un animal.
Dans cet exemple, la classe Animal
possède un attribut nom
et une méthode
__str__
qui retourne une chaîne de caractères décrivant l'animal. La méthode
__str__
est concrète, c'est-à-dire qu'elle a une implémentation par défaut.
Les méthodes manger
, dormir
et se_deplacer
sont abstraites et doivent être
redéfinies dans les classes filles. Comme précédemment, si on oublie de
redéfinir une méthode abstraite, Python lèvera une exception. Même chose si on
tente d'instancier un objet de type Animal
.
Il est donc possible de définir des classes abstraites qui sont à mi-chemin entre une interface et une classe concrète.