OUJOOD.COM
Imaginez que vous concevez un système de paiement : carte bancaire, virement, PayPal. Chaque méthode fonctionne différemment, mais toutes doivent pouvoir effectuer un paiement et afficher un reçu. Comment garantir que chaque développeur qui ajoute une nouvelle méthode de paiement n'oublie pas d'implémenter ces deux fonctionnalités ?
C'est exactement le problème que résolvent les classes abstraites. Une classe abstraite définit un contrat : elle liste les méthodes que toute sous-classe doit obligatoirement implémenter. Si une sous-classe oublie une méthode, Python lève une erreur à l'instanciation — avant même que le code s'exécute. Python implémente ce mécanisme via le module abc (Abstract Base Classes).
Créer une classe abstraite avec ABC
Pour créer une classe abstraite, héritez de ABC et décorez les méthodes obligatoires avec @abstractmethod. Une classe abstraite ne peut pas être instanciée directement.
from abc import ABC, abstractmethod
class Forme(ABC):
@abstractmethod
def aire(self):
pass
@abstractmethod
def perimetre(self):
pass
# Impossible d'instancier une classe abstraite
try:
f = Forme()
except TypeError as e:
print(e)
# Résultat : Can't instantiate abstract class Forme
# with abstract methods aire, perimetre
Implémenter les méthodes abstraites dans les sous-classes
Chaque sous-classe concrète doit implémenter toutes les méthodes abstraites. Si elle en oublie une, Python refuse de l'instancier.
from abc import ABC, abstractmethod
import math
class Forme(ABC):
@abstractmethod
def aire(self):
pass
@abstractmethod
def perimetre(self):
pass
def description(self):
# Méthode non abstraite : partagée par toutes les sous-classes
return f"Aire : {self.aire():.2f}, Périmètre : {self.perimetre():.2f}"
class Cercle(Forme):
def __init__(self, rayon):
self.rayon = rayon
def aire(self):
return math.pi * self.rayon ** 2
def perimetre(self):
return 2 * math.pi * self.rayon
class Rectangle(Forme):
def __init__(self, largeur, hauteur):
self.largeur = largeur
self.hauteur = hauteur
def aire(self):
return self.largeur * self.hauteur
def perimetre(self):
return 2 * (self.largeur + self.hauteur)
c = Cercle(5)
print(c.description())
# Résultat : Aire : 78.54, Périmètre : 31.42
r = Rectangle(4, 6)
print(r.description())
# Résultat : Aire : 24.00, Périmètre : 20.00
Notez que description() est une méthode normale dans la classe abstraite — elle est héritée par toutes les sous-classes. Les classes abstraites peuvent mélanger méthodes abstraites et méthodes concrètes.
Erreur si une méthode abstraite est oubliée
Si une sous-classe n'implémente pas toutes les méthodes abstraites, Python l'empêche d'être instanciée.
from abc import ABC, abstractmethod
class Forme(ABC):
@abstractmethod
def aire(self):
pass
@abstractmethod
def perimetre(self):
pass
# Triangle oublie perimetre() → Python refuse de l'instancier
class Triangle(Forme):
def __init__(self, base, hauteur):
self.base = base
self.hauteur = hauteur
def aire(self):
return 0.5 * self.base * self.hauteur
# perimetre() non implémentée !
try:
t = Triangle(3, 4)
except TypeError as e:
print(e)
# Résultat : Can't instantiate abstract class Triangle
# with abstract method perimetre
Exemple concret : système de paiement
Un exemple plus proche d'un projet réel : une classe abstraite ModePaiement qui force chaque implémentation à définir comment effectuer un paiement et comment rembourser.
from abc import ABC, abstractmethod
class ModePaiement(ABC):
@abstractmethod
def payer(self, montant):
pass
@abstractmethod
def rembourser(self, montant):
pass
def afficher_recu(self, montant):
# Méthode partagée par tous les modes de paiement
print(f"Reçu : {montant}€ via {self.__class__.__name__}")
class CarteBancaire(ModePaiement):
def __init__(self, numero):
self.numero = numero
def payer(self, montant):
print(f"Paiement de {montant}€ par carte se terminant par {self.numero[-4:]}")
self.afficher_recu(montant)
def rembourser(self, montant):
print(f"Remboursement de {montant}€ sur la carte {self.numero[-4:]}")
class PayPal(ModePaiement):
def __init__(self, email):
self.email = email
def payer(self, montant):
print(f"Paiement de {montant}€ via PayPal ({self.email})")
self.afficher_recu(montant)
def rembourser(self, montant):
print(f"Remboursement de {montant}€ sur le compte PayPal {self.email}")
# Le code appelant traite les deux types de la même façon
paiements = [
CarteBancaire("1234567890123456"),
PayPal("alice@exemple.com")
]
for mode in paiements:
mode.payer(49.99)
Propriété abstraite avec @property et @abstractmethod
On peut combiner @property et @abstractmethod pour forcer les sous-classes à implémenter un attribut calculé.
from abc import ABC, abstractmethod
class Animal(ABC):
@property
@abstractmethod
def son(self):
pass
def parler(self):
print(f"L\'animal fait : {self.son}")
class Chien(Animal):
@property
def son(self):
return "Woof !"
class Chat(Animal):
@property
def son(self):
return "Miaou !"
chien = Chien()
chien.parler()
# Résultat : L'animal fait : Woof !
chat = Chat()
chat.parler()
# Résultat : L'animal fait : Miaou !
Vérifier si une classe est abstraite ou concrète
from abc import ABC, abstractmethod
import inspect
class Forme(ABC):
@abstractmethod
def aire(self):
pass
class Cercle(Forme):
def aire(self):
return 3.14 * 5 ** 2
# Vérifier si une classe est abstraite
print(inspect.isabstract(Forme))
# Résultat : True
print(inspect.isabstract(Cercle))
# Résultat : False
# Vérifier l'appartenance
print(isinstance(Cercle(), Forme))
# Résultat : True
Par carabde | Mis à jour le 21 avril 2026