Programmation orientée objet (POO)
Prérequis
Avant de vous plonger dans cette leçon, vous devez être familiarisé avec :
- Les bases de Python
- Le travail avec les fonctions
Objectifs pédagogiques
À la fin de cette leçon, vous serez capable de :
- Définir et créer des classes et des objets
- Implémenter l'héritage
La Programmation Orientée Objet (POO) est un style de programmation fondamental pris en charge par Python. Elle repose sur deux concepts clés : les objets et les classes.
- Une classe est comme un plan ou un modèle qui définit les caractéristiques et le comportement d’un groupe d’objets.
- Un objet, quant à lui, est une instance concrète de cette classe : il rassemble des données (ses attributs) et des actions (ses méthodes) en une seule unité.
Vous utilisez probablement déjà des objets en Python, même sans en être conscient. Par exemple, une liste ou un dictionnaire est un objet. Vous appelez leurs méthodes (append, get, etc.) pour interagir avec eux.
De plus, les bibliothèques Python que vous utilisez souvent, comme Seaborn, Matplotlib ou Pandas, reposent sur les principes de la POO. Comprendre comment fonctionnent les objets et les classes vous permettra d’utiliser ces outils plus efficacement et de concevoir votre propre code de manière modulaire et réutilisable.
La POO est également très utile pour modéliser des concepts du monde réel dans vos programmes. Par exemple, vous pouvez créer une classe représentant une voiture avec des attributs comme la couleur et des méthodes comme démarrer ou freiner.
Dans cette leçon, vous apprendrez à maîtriser ces concepts essentiels pour écrire un code Python plus structuré, clair et efficient.
Une classe est comme un plan ou un modèle qui indique comment créer quelque chose. Elle définit la structure de base, comme les informations/données qu'elle doit avoir (attributs) et ce qu'elle peut faire (méthodes).
Une fois que vous avez une classe, vous pouvez l'utiliser pour créer différentes versions de cette chose, appelées objets.
Supposons que nous voulions modéliser un Client, nous pourrions construire une classe qui décrit les détails de base que chaque client devrait avoir comme id, une liste d'achats et les actions qu'il peut effectuer comme ajouter un nouvel achat.
Créons la classe la plus simple pour un Client :
class Customer:
"""Describe the Customer"""
pass
class Customer: Cette ligne définit une classe nomméeCustomer(suivant la convention d'utiliser CamelCase)pass: L'instructionpassest utilisée comme espace réservé
Cette classe est un modèle vide, ce qui signifie que vous pouvez créer des objets à partir d'elle, mais ces objets n'auront ni attributs ni méthodes.
Pour créer un objet client — ce processus est aussi appelé instanciation — vous utilisez le nom de la classe suivi de parenthèses Customer() :
# Create a Customer object named customer1
customer1 = Customer() # Instantiation
# Create another Customer object named customer2
customer2 = Customer() # Instantiation
# Compare if customer1 and customer2 are the same object
# This will return False because they are separate instances
print(customer1 == customer2)
# Output:
False
En utilisant la notation pointée, nous pouvons ajouter un attribut identifiant à chaque client :
customer1.iD = 10
customer2.iD = 20
Dans ce code :
customer1.iD = 10: Nous créons (ou mettons à jour) un attribut nomméiDsur l'objetcustomer1et lui assignons la valeur10.customer2.iD = 20: De même, ceci crée (ou met à jour) l'attributiDsurcustomer2et lui assigne la valeur20.
Une approche plus pratique consiste à définir les attributs dans la définition de la classe, généralement dans la méthode __init__(). Une méthode est une fonction qui fait partie d'une classe. Tout ce que vous avez appris sur les fonctions s'applique également aux méthodes.
Définir les attributs avec __init__()
La méthode __init__() est une fonction spéciale qui s'exécute automatiquement lorsque nous créons une nouvelle instance. Elle initialise les attributs d'un objet lors de sa création. Ajoutons cette méthode à notre classe Customer afin que chaque nouvel objet Customer soit créé avec les attributs iD et purchase_list déjà définis.
class Customer:
def __init__(self, iD):
self.iD = iD # Create an attribute 'iD' and set it to the given value
self.purchase_list = [] # Create an attribute 'purchase_list' and set it to the empty list
# Usage
customer = Customer(10)
print(customer.iD) # Output: 10
print(customer.purchase_list) # Output: []
Examinons ce code :
- Définition de la méthode
__init__():self: Ce paramètre requis doit venir en premier. Il fait référence à l'objet (ou instance) spécifique de la classe en cours de création.selfs'assure que les attributs appartiennent à cet objet particulier.iD: Ce paramètre est passé lors de la création d'un nouvel objet client pour initialiser un attribut.
self.iD = iD: Cela crée l'attributiDde l'objet et l'initialise à la valeur du paramètreiD(similaire àcustomer1.iD = 10).self.purchase_list = []: Cela crée l'attributpurchase_listde l'objet et l'initialise à une liste vide.- Création de l'objet
Customer:customer = Customer(10): Ici, nous créons un nouvel objet à partir de la classeCustomeret passons10comme valeur pouriD.
- Accès aux attributs :
print(customer.iD)affiche10car l'attributiDa été défini à10lors de la création de l'objet.print(customer.purchase_list)affiche[](une liste vide) car aucun achat n'a encore été ajouté.
Définir les méthodes : comportement des objets
Les méthodes définissent ce qu'un objet peut faire. Elles sont similaires à des fonctions mais sont liées à une classe spécifique. Ajoutons quelques méthodes à notre classe Customer pour permettre d'ajouter, de supprimer et d'effacer l'historique des achats du client.
class Customer:
def __init__(self, iD):
self.iD = iD # Create an attribute 'iD' and set it to the given value
self.purchase_list = [] # Initialize the purchase list as an empty list
def add_purchase(self, item):
"""Adds an item to the purchase list."""
self.purchase_list.append(item)
def remove_purchase(self, item):
"""Removes an item from the purchase list if it exists."""
if item in self.purchase_list:
self.purchase_list.remove(item)
else:
print(f"{item} not found in purchase list.")
def clear_purchase_history(self):
"""Clears the entire purchase list."""
self.purchase_list.clear()
# Creating a customer object
customer = Customer(10)
# Adding some purchases
customer.add_purchase("Book")
customer.add_purchase("Laptop")
print(customer.purchase_list) # Output: ['Book', 'Laptop']
# Removing a purchase
customer.remove_purchase("Book")
print(customer.purchase_list) # Output: ['Laptop']
# Clearing the purchase history
customer.clear_purchase_history()
print(customer.purchase_list) # Output: []
print(customer)
Voici les points clés concernant les méthodes dans le code ci-dessus :
- Méthodes avec paramètres :
Les méthodes comme add_purchase et remove_purchase prennent un paramètre supplémentaire, item, en plus de self. Cela permet aux méthodes de recevoir des données spécifiques (comme "Livre" ou "Ordinateur portable") lorsqu'elles sont appelées, permettant ainsi d'interagir avec les attributs de l'objet.
- Logique interne :
Les méthodes contiennent une logique pour garantir un comportement correct. Par exemple, remove_purchase vérifie si l'élément existe dans la purchase_list avant d'essayer de le supprimer, évitant ainsi les erreurs et offrant un comportement contrôlé.
- Modification de l'état :
Les méthodes peuvent modifier l'état interne de l'objet. Dans ce cas, des méthodes comme add_purchase et clear_purchase_history modifient la purchase_list en ajoutant des éléments, en les supprimant ou en effaçant complètement la liste, affectant directement l'état de l'objet Customer.
- Retour d'information :
Certaines méthodes fournissent des retours d'information. Par exemple, remove_purchase affiche un message si l'élément n'est pas trouvé dans la liste, donnant un feedback clair à l'utilisateur sur ce qui se passe dans l'objet.
La méthode spéciale __str__
La méthode __str__ est une méthode spéciale en Python qui définit comment un objet doit être représenté en tant que chaîne de caractères. Elle permet de contrôler comment l'objet est affiché lors de l'utilisation de print() ou str().
class Customer:
def __init__(self, iD):
self.iD = iD
self.purchase_list = []
def add_purchase(self, item):
self.purchase_list.append(item)
def remove_purchase(self, item):
if item in self.purchase_list:
self.purchase_list.remove(item)
else:
print(f"{item} not found in purchase list.")
def clear_purchase_history(self):
self.purchase_list.clear()
def __str__(self):
return f"Customer ID: {self.iD}, Purchases: {self.purchase_list}"
# Creating a customer object
customer = Customer(10)
# Adding some purchases
customer.add_purchase("Book")
customer.add_purchase("Laptop")
print(customer.purchase_list) # Output: ['Book', 'Laptop']
# Removing a purchase
customer.remove_purchase("Book")
print(customer.purchase_list) # Output: ['Laptop']
# Clearing the purchase history
customer.clear_purchase_history()
print(customer.purchase_list) # Output: []
# Using the __str__ method
print(customer) # Output: Customer ID: 10, Purchases: []
La méthode __str__(self) renvoie une chaîne de caractères qui affiche l'ID du client et sa liste d'achats actuelle.
Exercice
Construire une classe Employee
Dans cet exercice, vous allez créer une classe Employee pour modéliser les employés d'une entreprise. La classe suivra des informations telles que le nom de l'employé, son poste, son salaire et son statut (s'il est actuellement employé). Vous ajouterez également des méthodes pour permettre la mise à jour de son salaire, le changement de son poste et le fait de le marquer comme n'étant plus employé.
Étapes :
- Créer la classe
Employee:- La classe doit avoir les attributs suivants :
name: Le nom de l'employé.position: Le titre du poste ou la fonction de l'employé (par exemple, "Gestionnaire").salary: Le salaire de l'employé (par exemple, 60 000).employed: Une valeur booléenne indiquant si l'employé est actuellement employé (par défaut :True).
- La classe doit avoir les attributs suivants :
- Définir la méthode
__init__():- Cette méthode doit initialiser les attributs
name,position,salaryetemployedlorsqu'un nouvel objet employé est créé.
- Cette méthode doit initialiser les attributs
- Ajouter les méthodes suivantes :
give_raise(self, amount): Une méthode qui augmente le salaire de l'employé du montant donné.change_position(self, new_position): Une méthode pour mettre à jour le poste de l'employé ennew_position.terminate(self): Une méthode qui marque l'employé comme n'étant plus employé en définissantemployedsurFalse.__str__(self): Une méthode pour afficher les détails de l'employé, y compris son nom, son poste, son salaire et son statut d'emploi.
Gestion des employés
Dans cet exercice, vous utiliserez la classe Employee pour créer et gérer les employés d'une entreprise. Vous créerez des employés, mettrez à jour leurs salaires, les promouvrez à de nouveaux postes et gérerez leur statut d'emploi.
Étapes :
-
Créer 3 objets employés avec les détails suivants :
- Employé 1 : Nom = "Alice Smith", Poste = "Développeur", Salaire = 70 000
- Employé 2 : Nom = "Bob Johnson", Poste = "Designer", Salaire = 55 000
- Employé 3 : Nom = "Charlie Lee", Poste = "Chef de projet", Salaire = 80 000
-
Imprimer les détails de ces 3 employés.
-
Donner une augmentation de 5 000 $ à Alice et la promouvoir au poste de "Développeur senior".
-
Mettre fin à l'emploi de Bob en utilisant la méthode
terminate(). -
Vérifier si le salaire de Charlie est supérieur à 75 000 $. Si c'est le cas, lui donner une augmentation de 2000 dollars
-
Imprimer les détails mis à jour des 3 employés.
Dans cet exercice, vous allez créer une classe Employee pour modéliser les employés d'une entreprise. La classe devra gérer des informations comme le nom de l'employé, son poste, son salaire et son statut (s'il est actuellement employé).
Solution
Voir la solution
Construire une classe Employee
class Employee:
def __init__(self, name, position, salary):
self.name = name
self.position = position
self.salary = salary
self.employed = True
def give_raise(self,amount):
"""Increase the salary by the given amount."""
self.salary = self.salary + amount
def change_position(self,new_position):
"""Update the employee's position."""
self.position = new_position
def terminate(self):
"""Mark the employee as no longer employed."""
self.employed = False
def __str__(self):
"""Return a string representation of the employee's details."""
return f"Employee: {self.name}, Position: {self.position}, Salary: {self.salary}, Employed: {self.employed}"
Voir la solution
Gestion des employés
# Step 1: Create 3 employee objects
employee1 = Employee("Alice Smith", "Developer", 70000)
employee2 = Employee("Bob Johnson", "Designer", 55000)
employee3 = Employee("Charlie Lee", "Project Manager", 80000)
# Step 2: Print the initial details of all employees
print(employee1)
print(employee2)
print(employee3)
# Step 3: Give Alice a raise and promote her
employee1.give_raise(5000)
employee1.change_position("Senior Developer")
# Step 4: Terminate Bob's employment
employee2.terminate()
# Step 5: Check Charlie's salary and give him a raise if it's above 75,000
if employee3.salary > 75000:
employee3.give_raise(2000)
# Step 6: Print the updated details of all employees
print(employee1)
print(employee2)
print(employee3)
L'héritage permet de créer des relations hiérarchiques entre les classes, où les classes enfants (aussi appelées sous-classes) héritent des attributs et méthodes de leurs classes parentes (aussi appelées superclasses). Cela permet aux classes enfants de réutiliser et d'étendre les fonctionnalités des classes parentes sans dupliquer le code.
Imaginons que nous voulions créer un type spécialisé de client, appelé VIPCustomer, qui reçoit des avantages supplémentaires, comme un taux de remise et la possibilité de suivre les dépenses totales. Au lieu de créer une nouvelle classe à partir de zéro, nous pouvons utiliser l'héritage pour construire sur la base de la classe Customer existante.
Pour implémenter l'héritage en Python, nous passons la classe parente (Customer) entre parenthèses lors de la définition de la classe enfant (VipCustomer) :
class VipCustomer(Customer):
def __init__(self, iD, discount_rate):
super().__init__(iD) # Call the parent class constructor to inherit basic attributes
self.discount_rate = discount_rate # Discount rate specific to VIP customers
self.total_spent = 0 # Track the total amount spent by the VIP customer
def add_purchase(self, item, amount_spent):
"""Add an item to the purchase list and update the total spent."""
super().add_purchase(item) # Use the parent class's add_purchase method
self.total_spent += amount_spent # Update the total amount spent by the VIP customer
def apply_discount(self):
"""Apply the discount to the total amount spent."""
discount = self.total_spent * (self.discount_rate / 100)
return self.total_spent - discount
def __str__(self):
"""String representation of a VIP customer, showing ID, discount rate, and total spent."""
return f"VIPCustomer ID: {self.iD}, Total Spent: €{self.total_spent}, Discount Rate: {self.discount_rate}%"
1. Définition de la classe :
class VipCustomer(Customer): - Cette ligne définit une nouvelle classe appelée VipCustomer qui hérite de la classe Customer. VipCustomer aura tous les attributs et méthodes de Customer, plus tout autre élément supplémentaire que nous définissons.
2. Méthode __init__ :
def __init__(self, iD, discount_rate): - Cette méthode initialise un nouvel objet VipCustomer. Elle prend trois paramètres :
self(l'instance en cours de création)iD(l'identifiant du client)discount_rate(taux de remise en pourcentage)super().__init__(iD): Cela appelle la méthode__init__de la classe parenteCustomerpour initialiser les attributs de base (commeiD) que tous les clients possèdent.self.discount_rate = discount_rate: Cette ligne définit l'attributdiscount_ratespécifique aux clients VIP.self.total_spent = 0: Cela initialise un nouvel attribut pour suivre le montant total dépensé par le client VIP.
3. Méthode add_purchase :
def add_purchase(self, item, amount_spent):
Cette méthode ajoute un achat au dossier du client. Elle prend l'article et le montant dépensé comme paramètres. Elle remplace la méthode de la classe parente.
super().add_purchase(item)
Cela appelle la méthode add_purchase de la classe parente Customer pour ajouter l'article à la liste des achats.
self.total_spent += amount_spent
Cela met à jour l'attribut
total_spent en ajoutant le montant du nouvel achat.
4. Méthode apply_discount :
def apply_discount(self):
Cette méthode calcule et applique la remise au montant total dépensé.
Le choix de super().__init__(iD) est utilisé pour éviter la duplication de code et initialiser les attributs de base de la classe parente Customer. En appelant la méthode __init__ de la classe parente, on s'assure que les attributs communs (comme iD) sont correctement initialisés avant d'ajouter les attributs spécifiques à VipCustomer (discount_rate et total_spent). Cela respecte le principe DRY (Don't Repeat Yourself) et maintient une hiérarchie claire entre les classes.
Voici comment nous pourrions utiliser la classe VipCustomer dans la pratique :
# Create a VIP customer with ID 1 and a 15% discount rate
vip_customer = VIPCustomer(1, 15)
# Add purchases to the VIP customer's record
vip_customer.add_purchase("Laptop", 1000) # €1000 spent on a laptop
vip_customer.add_purchase("Phone", 500) # €500 spent on a phone
# Calculate and display the discounted total
discounted_total = vip_customer.apply_discount()
print(f"Total after discount: €{discounted_total:.2f}") # Output: Total after discount: €1275.00
# Display VIP customer details
print(vip_customer) # Output: VIPCustomer ID: 1, Total Spent: €1500, Discount Rate: 15%
# Display the purchase list
print(vip_customer.purchase_list) # Output: ['Laptop', 'Phone']
Pour aller plus loin : Inheritence and Composition
Exercice
Extension de la classe Employee
Dans cet exercice, vous allez créer une classe Intern qui hérite de la classe Employee. La classe Intern introduira de nouvelles fonctionnalités comme le suivi de la durée du stage et la vérification de son statut actif.
1. Créer la classe Intern :
- La classe Intern doit hériter de la classe Employee.
- Ajouter un nouvel attribut :
duration: La durée du stage (en mois).
2. Ajouter des méthodes :
check_internship_status(): Déterminer si le stage est en cours ou terminé.- Directives :
- Examiner l'attribut
duration, qui indique les mois de stage restants. - Si
durationest positif, le stage est en cours. Afficher les mois restants. - Si
durationest zéro, le stage est terminé. Afficher un message d'achèvement.
- Examiner l'attribut
reduce_duration(months): Diminuer la durée du stage d'un nombre spécifié de mois. S'assurer quedurationne devient pas négatif en le définissant à zéro si le résultat serait inférieur à zéro.
Solution
Voir la solution
Extension de la classe Employee
class Intern(Employee):
def __init__(self, name, position, salary, duration):
super().__init__(name, position, salary)
self.duration = duration # Duration of the internship in months
def check_internship_status(self):
"""Check if the internship is still ongoing."""
if self.duration > 0:
print(f"Internship has {self.duration} months left.")
else:
print("The internship is over.")
def reduce_duration(self, months):
"""Reduce the internship duration by the given number of months."""
self.duration = self.duration - months
if self.duration < 0:
self.duration = 0 # Ensure the duration doesn't go negative
# Example usage
intern = Intern("John Doe", "Marketing Intern", 2000, 6)
print(intern) # Using inherited __str__ method
intern.check_internship_status() # Output: Internship has 6 months left.
intern.reduce_duration(2)
intern.check_internship_status() # Output: Internship has 4 months left.
Excellent travail ! Dans cette leçon, vous avez découvert les classes, les objets et l'héritage — des concepts clés de la Programmation Orientée Objet (POO) qui vous aident à écrire du code organisé, réutilisable et modulaire.
Vous savez maintenant comment définir une classe en Python en utilisant le mot-clé class et comment créer des attributs et des méthodes au sein d'une classe. Ces éléments vous permettent de créer des objets avec des propriétés et des comportements spécifiques.
De plus, vous avez exploré comment créer des objets à partir de classes, accéder à leurs attributs et méthodes, et utiliser la méthode __init__() pour initialiser efficacement de nouveaux objets.
Par ailleurs, vous avez appris à créer des sous-classes (classes enfants) qui héritent des superclasses (classes parentes). Cela permet d'étendre ou de redéfinir les comportements et attributs, créant ainsi des classes plus spécialisées.
Continuez votre excellent travail !