Programmation orientée objet en Python
La POO organise un programme autour d’objets qui possèdent un état et exposent des comportements par une interface claire.
Qu’est-ce que la programmation orientée objet en Python ?
La POO modélise un système comme des objets qui collaborent. Chaque objet associe un état aux opérations autorisées, ce qui clarifie les responsabilités dans une grande application.
Les quatre principes fondamentaux
- Encapsulation : regrouper état et comportement et contrôler l’accès.
- Abstraction : exposer une interface utile et masquer les détails inutiles.
- Héritage : spécialiser une classe lorsqu’il existe une véritable relation « est un ».
- Polymorphisme : appliquer la même opération à différents objets avec un comportement adapté.
Conception de l’exemple
- BankAccount possède le solde et valide les dépôts.
- SavingsAccount réutilise le contrat de la classe de base.
- SavingsAccount redéfinit month_end pour appliquer les intérêts.
- La boucle traite tous les comptes via la même interface.
Exemple de base
class BankAccount:
def __init__(self, owner, balance=0):
self.owner = owner
self._balance = float(balance)
@property
def balance(self):
return self._balance
def deposit(self, amount):
if amount <= 0:
raise ValueError("Deposit must be positive")
self._balance += amount
def month_end(self):
return "No interest"
class SavingsAccount(BankAccount):
def __init__(self, owner, balance, interest_rate):
super().__init__(owner, balance)
self.interest_rate = interest_rate
def month_end(self):
self._balance *= 1 + self.interest_rate
return "Interest applied"
accounts = [
BankAccount("Ada", 100),
SavingsAccount("Lin", 200, 0.05),
]
accounts[0].deposit(50)
for account in accounts:
status = account.month_end()
print(f"{account.owner}: {account.balance:.2f} ({status})")
Résultat attendu
Ada: 150.00 (No interest)
Lin: 210.00 (Interest applied)
Les quatre principes de la POO en pratique
Ces exemples ciblés montrent comment Python met en œuvre l’encapsulation, l’abstraction, l’héritage et le polymorphisme dans du code courant.
Encapsulation
regrouper état et comportement et contrôler l’accès.
class BankAccount:
def __init__(self, balance=0):
# Double underscores prevent accidental direct access.
self.__balance = balance
@property
def balance(self):
# Expose read access without exposing mutation.
return self.__balance
def deposit(self, amount):
# Keep validation next to the state it protects.
if amount <= 0:
raise ValueError("Amount must be positive")
self.__balance += amount
account = BankAccount(100)
account.deposit(50)
print(account.balance) # 150
Abstraction
exposer une interface utile et masquer les détails inutiles.
from abc import ABC, abstractmethod
class PaymentMethod(ABC):
@abstractmethod
def pay(self, amount):
# Callers know what pay does, not how each provider does it.
pass
class CreditCard(PaymentMethod):
def pay(self, amount):
# The concrete class hides its payment implementation.
return f"Paid ${amount:.2f} by card"
payment = CreditCard()
print(payment.pay(25))
Héritage
spécialiser une classe lorsqu’il existe une véritable relation « est un ».
class Employee:
def __init__(self, name):
self.name = name
def describe(self):
return self.name
class Developer(Employee):
def __init__(self, name, language):
# Reuse initialization from the parent class.
super().__init__(name)
self.language = language
def describe(self):
# Extend inherited behavior instead of duplicating it.
return f"{super().describe()} writes {self.language}"
print(Developer("Ada", "Python").describe())
Polymorphisme
appliquer la même opération à différents objets avec un comportement adapté.
class EmailNotification:
def send(self, message):
return f"Email: {message}"
class SmsNotification:
def send(self, message):
return f"SMS: {message}"
def notify(channels, message):
for channel in channels:
# The same call selects behavior from each concrete object.
print(channel.send(message))
channels = [EmailNotification(), SmsNotification()]
notify(channels, "Order shipped")
Fonctionnement
BankAccount encapsule le solde. SavingsAccount hérite de l’interface et redéfinit month_end. La boucle utilise les deux objets de façon uniforme et Python choisit la bonne méthode à l’exécution.
Modifiez les valeurs et exécutez le programme avec le compilateur Python en ligne CodeUtility, sans installation locale.
Exercices pratiques
Modifiez les entrées et testez les cas limites avant d’utiliser des données plus volumineuses.
- Prédisez la sortie avant l’exécution.
- Gérez les entrées vides ou invalides.
- Placez la logique dans une fonction et ajoutez des tests.