Bases de Python

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

  1. BankAccount possède le solde et valide les dépôts.
  2. SavingsAccount réutilise le contrat de la classe de base.
  3. SavingsAccount redéfinit month_end pour appliquer les intérêts.
  4. La boucle traite tous les comptes via la même interface.
Conseil : Préférez la composition lorsqu’un objet en utilise simplement un autre. Réservez l’héritage aux relations de substitution valides.

Exemple de base

Exécuter le code →
main.py
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

Exécuter le code →

regrouper état et comportement et contrôler l’accès.

encapsulation.py
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

Exécuter le code →

exposer une interface utile et masquer les détails inutiles.

abstraction.py
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

Exécuter le code →

spécialiser une classe lorsqu’il existe une véritable relation « est un ».

inheritance.py
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

Exécuter le code →

appliquer la même opération à différents objets avec un comportement adapté.

polymorphism.py
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.

  1. Prédisez la sortie avant l’exécution.
  2. Gérez les entrées vides ou invalides.
  3. Placez la logique dans une fonction et ajoutez des tests.
Exécuter dans l’IDE Python →