Programmation Orientée Objet
Introduction
La POO est un paradigme de programmation qui organise le code autour d’objets plutôt que de fonctions et de procédures.
Ces objets représentent des entités du monde réel, ils ont :
- des attributs : ce qu’ils sont
- des méthodes : ce qu’ils peuvent faire
Intérêt
La POO permet :
- d’organiser le code de manière plus structurée
- de favoriser la réutilisation et la maintenance du code
- de modéliser les concepts du domaine d’application de manière naturelle
Elle est largement utilisée dans de nombreux langages de programmation, dont Python, pour développer des applications complexes et évolutives.
Exemple
Modélisons par exemple une personne :
- Attributs : nom, prenom, age, compétences
- Méthodes : apprendre(), vieillir()
La méthode vieillir() va augmenter l’age de la personne de 1.
La méthode apprendre(“Python”), va ajouter “Python” à la liste des compétences.
Principes fondamentaux
Encapsulation
L’encapsulation consiste à regrouper les données et les méthodes qui les manipulent au sein d’un même objet.
Cela permet de :
- cacher les détails d’implémentation
- de fournir une interface cohérente pour interagir avec l’objet
Héritage
L’héritage permet de créer de nouvelles classes à partir de classes existantes, en héritant de leurs attributs et méthodes. Cela favorise :
- la réutilisation du code
- la création d’une hiérarchie de classes
Exemple : Carré hérite de Rectangle. Un carré est un rectangle avec des propriétés supplémentaires.
Polymorphisme
Le polymorphisme permet à des objets de classes différentes de répondre de manière différente à une même action.
Cela permet de manipuler des objets de différentes classes de manière uniforme, en utilisant des interfaces communes.
Classe et Objet
- Une classe est un modèle pour créer des objets
- Elle définit les attributs et les méthodes qui seront présents dans chaque objet de cette classe
- Les objets sont des instances de classes
- Ils possèdent des attributs et des méthodes qui définissent leur état et leur comportement
Exemple
- Création de la classe Personne qui représente une personne
- avec ses attributs et ses méthodes
- Création de l’objet personne1 à partir de cette classe
- prénom = Sacha
- nom = Touille
- age = 20
- compétences = []
Classe Python
personne.py
class Personne:
def __init__(self, nom, prenom, age):
self.nom = nom
self.prenom = prenom
self.age = age
self.competences = []
def apprendre(self, nouvelle_competence):
"""Ajoute une nouvelle compétence à la liste des compétences."""
self.competences.append(nouvelle_competence)
def vieillir(self):
"""Incrémente l'âge de la personne d'un an."""
self.age += 1
Objets Python
À partir de la classe Personne, nous pouvons créer des objets.
main.py
from personne import Personne
= Personne("Touille", "Sacha", 20)
personne1 = Personne("Ginal", "Laury", 30)
personne2
personne2.vieillir()"SQL") personne2.apprendre(
Constructeur
Le code Personne("Touille", "Sacha", 20)
appelle la méthode __init__()
de la classe Personne.
Cette méthode est appelée le constructeur.
Comme son nom l’indique, elle permet de « construire » des objets à partir de la classe.
Conventions Python
- Un nom de classe s’écrit en CamelCase (majuscule à chaque mot)
- Un fichier python (ou module) ne contient qu’une seule classe
- Un nom de module s’écrit en snake_case (mots en minuscules séparés par des
_
)
velo_electrique.py
class VeloElectrique:
...
Héritage
Un des trois piliers de la POO est l’héritage.
Une classe enfant peut utiliser tous les attributs et méthodes de sa classe mère.
Ce principe d’héritage permet également de mettre en commun des attributs et méthodes pour éviter des duplications de code.
Exemple d’Héritage
Supposons que dans notre code, nous voulons gérer des vélos et de trottinettes.
L’idée naïve est de créer une classe pour chacun.
Exemple d’Héritage
velo.py
class Velo:
def __init__(self, couleur, nb_pignons):
self.couleur = couleur
self.nb_pignons = nb_pignons
self.vitesse = 0 # La vitesse initiale est 0
def accelerer(self, increment):
"""Augmente la vitesse du vélo."""
self.vitesse += increment
def ralentir(self, decrement):
"""Diminue la vitesse du vélo."""
self.vitesse -= decrement
if self.vitesse < 0:
self.vitesse = 0 # La vitesse ne peut pas être négative
trottinette.py
class Trottinette:
def __init__(self, couleur, nb_pignons):
self.couleur = couleur
self.vitesse = 0 # La vitesse initiale est 0
def accelerer(self, increment):
"""Augmente la vitesse."""
self.vitesse += increment
def ralentir(self, decrement):
"""Diminue la vitesse."""
self.vitesse -= decrement
if self.vitesse < 0:
self.vitesse = 0 # La vitesse ne peut pas être négative
Exemple d’Héritage
En réfléchissant un peu, nous nous disons que ces 2 classes ont des attributs et méthodes en commun :
- couleur
- vitesse
- accelerer()
- ralentir()
Une idée est de regrouper ces caractéristiques communes dans une classe DeuxRoues
. Puis de faire hériter Vélo
et Trottinette
de DeuxRoues
Exemple d’Héritage
deux_roues.py
class DeuxRoues:
def __init__(self, couleur):
self.couleur = couleur
self.vitesse = 0 # La vitesse initiale est 0
def accelerer(self, increment):
"""Augmente la vitesse."""
self.vitesse += increment
def ralentir(self, decrement):
"""Diminue la vitesse."""
self.vitesse -= decrement
if self.vitesse < 0:
self.vitesse = 0 # La vitesse ne peut pas être négative
velo.py
from deux_roues import DeuxRoues
class Velo(DeuxRoues):
def __init__(self, couleur, nb_pignons):
super().__init__(couleur) # Appel du constructeur de la classe mère
self.nb_pignons = nb_pignons
trottinette.py
from deux_roues import DeuxRoues
class Trottinette(DeuxRoues):
def __init__(self, couleur, nb_pignons):
super().__init__(couleur) # Appel du constructeur de la classe mère
Self et Super
- self est une référence à l’instance actuelle de la classe
- Il est utilisé pour accéder aux attributs et aux méthodes d’une instance spécifique de la classe
- super() est une fonction intégrée qui référence la classe mère
- Il est utilisé pour appeler des méthodes de la classe parent dans une classe enfant
Classe Abstraite
- Est-ce que cela a du sens de créer un objet à partir de la classe DeuxRoues ?
Si l’on vous demande de dessiner un deux-roues, vous ne savez pas vraiment comment faire, vous manquez d’informations.
Alors que dessiner un vélo ou une trottinette, c’est déjà plus concret.
Classe Abstraite - DeuxRoues
Certaines classes n’ont pas vocation à être instanciées. Par exemple, nous n’allons pas créer d’objets de la classe DeuxRoues.
Nous allons directement créer des Velo et des Trottinettes.
Nous pouvons donc définir la classe DeuxRoues comme abstraite.
- on ne peut pas créer d’objets à partir de cette classe
Classe Abstraite - DeuxRoues
La principale utilité des classes abstraites est de définir un contrat pour les classes filles.
Elles fournissent une structure et une organisation cohérentes pour les classes qui partagent des caractéristiques communes, tout en permettant une flexibilité pour les implémentations spécifiques à chaque classe fille.
Classe Abstraite - Python
En Python, la notion de classe abstraite est implémentée grâce au module abc (Abstract Base Classes).
Ce module fournit le décorateur @abstractmethod
qui permet de déclarer une méthode comme abstraite dans une classe abstraite.
Une classe abstraite est définie en héritant de la classe ABC du module abc.
Classe Abstraite - Remarque
Classe mère ne veut pas dire forcément classe abstraite.
Par exemple :
- Considérons par exemple une classe
VeloElectrique
qui hérite de la classeVelo
- Cela parait cohérent car un vélo électrique est un vélo
- donc il hérite de tous ses attributs et méthodes
- et il a des caractéristiques supplémentaires (batterie, autonomie, puissance…)
- Or ici la classe
Velo
n’a pas vocation à être abstraite car il n’est pas absurde de créer un objet vélo