Skip to content

Paradigme orienté objets

Définition

En programmation orientée objet, un programme est un ensemble d'entités qui interagissent. Ces entités sont appelées des objets. Un objet possède un état (les données qui le caractérisent) et des comportements (ce qu'il sait faire).

L'état d'un objet est représenté par l'ensemble de ses attributs Les comportements d'un objet sont représentés par l'ensemble de ses méthodes

Une classe permet de définir une famille d'objets. A partir d'une classe, on peut créer autant d'objets que l'on veut. Ce sont des exemplaires, des instances de la classe.

Classe Animal

Voici un exemple basique d'une classe en Python qui modélise la phrase "Un animal porte un nom, et il sait parler."

class Animal:
    def __init__(self, nom: str):
        self.nom = nom  # Attribut d'instance

    def parler(self):   # Méthode d'instance
        print(f"{self.nom} fait du bruit.")

x = Animal("Bidule")   # création d'une instance d'Animal portant le nom bidule
print(x.nom)           # On accède aux attributs de l'instance avec le point. Affiche: Bidule
x.nom = "Truc"         # Mutabilité -> On peut modifier les attributs de l'instance de la même manière 
x.parler()  # On accède aussi aux méthodes d'instance par le point. Affiche: Truc fait du bruit.

Ce programme affichera

Bidule
Truc fait du bruit

Dans cet exemple :

  • __init__ : Cette méthode sera automatiquement chargée d'initialiser les attributs de l'objet en cours d'instanciation. On le considère comme le constructeur de la classe.
  • self : Représente l'instance actuelle de la classe. Il permet d'accéder aux attributs et méthodes de l'objet lui-même.

Constructeur

Un constructeur est une fonction particulière appelée lors de l'instanciation. Elle permet d'allouer la mémoire nécessaire à l'objet et d'initialiser ses attributs.

Abus de langage

Il est très (trop) courant de considérer __init__ comme le constructeur en Python, car il initialise l'objet après sa création. Cependant, il ne fait que la moitié du travail. la méthode chargée de créer l'instance est le dunder __new__. La preuve en est que __init__ prend self en paramètre, c'est donc bien que self existe déjà avant l'invocation d'__init__. Le constructeur est en réalité le couple formé par les méthodes __new__ et __init__.

Voici une portion de code pour se rendre compte de ce qu'il se passe réellement:

class MaClasse:
    def __new__(cls, *args, **kwargs):
        '''Appel de __new__ pour créer une nouvelle instance de la classe'''
        print("Appel de __new__ : Création de l'instance")
        instance = super(MaClasse, cls).__new__(cls)
        return instance

    def __init__(self, valeur):
        '''Appel de __init__ pour initialiser l'instance avec les attributs'''
        print("Appel de __init__ : Initialisation de l'instance")
        self.valeur = valeur

    def afficher_valeur(self):
        print(f"Valeur : {self.valeur}")

# Instanciation d'un objet
c = MaClasse("truc")
c.afficher_valeur()

On considèrera malgré tout que __init__ est le constructeur, car si on vous pose la question, c'est la réponse attendue.

Interactions entre objets

Rajoutons la phrase "Un Humain porte un nom, un prenom et peut adopter des animaux domestiques. On peut afficher la liste des animaux d'un Humain".

class Humain:
    def __init__(self, prenom: str, nom: str):
        self.nom = nom
        self.prenom = prenom
        self.animaux: list[Animal] = []

    def adopte(self, a: Animal):
        self.animaux.append(a)

    def afficher_animaux(self):
        for a in self.animaux:
            print(a.nom)


x = Humain("Peter", "Quill")
y = Animal("Rocket")
x.adopte(y)
x.afficher_animaux()   # Affiche Rocket

Ici, on a choisi que l'humain porte la liste de ses animaux. On aurait pu à la place ajouter un attribut propriétaire à la classe Animal pour dire qu'un animal est la propriété d'un humain particulier. Mais on aurait perdu la possibilité d'afficher les animaux d'un humain sans disposer de la liste de tous les animaux. Il faut souvent choisir quelle classe est le "chef d'orchestre" en fonction du problème qu'on a à traiter.

Héritage

L’héritage est un autre pilier fondamental en POO. Il permet à une classe (dite sous classe) d’hériter des attributs et méthodes d’une autre classe (dite super classe).

Exemple : Classe RatonLaveur héritant de Animal

Nous pourrions ajouter un attribut espece à la classe Animal pour les distinguer, mais ici, pour introduire le concept, nous allons choisir cette phrase en Français: "Un RatonLaveur EST UN Animal"

class RatonLaveur(Animal):

    def __init__(self, nom: str):
        super().__init__(nom) # cette ligne appelle le constructeur de la super classe

    def parler(self):
        print(f"{self.nom} émet des grognements.")

La classe RatonLaveur hérite de Animal mais peut redéfinir certaines méthodes (ici, parler), ou encore en créer d'autres. Ainsi, un objet de la classe Chien pourra aboyer au lieu de "faire du bruit". Un Raton laveur pourra émettre das grognements

En français, on peut très bien dire dès lors "Un Chien EST UN Animal"

Programme exemple:

x = Animal("Animal quelconque")
x.parler()
y = RatonLaveur("Rocket")
y.parler()
print(isinstance(x, Animal), isinstance(x, RatonLaveur))
print(isinstance(y, Animal), isinstance(y, RatonLaveur))
Affichage:
Animal quelconque fait du bruit
Rocket émet des grognements
True False
True True

La fonction isinstance sert à savoir si un objet est une instance d'une classe. Si on a besoin de l'utiliser hors debugging, c'est sûrement du à un défaut de conception. On n'aura pas besoin de l'utiliser.

Modélisation objet

Une voiture a une marque, un modèle, un prix, un nombre de kilomètres parcourus, une quantite de carburant dans le réservoir (en litres), et une consommation (litres au 100). Une voiture peut rouler sur une certaine distance. À chaque fois qu'elle roule, le kilométrage augmente, et le carburant diminue. Le réservoir de la voiture a une capacité maximale. On peut afficher les caractéristiques de la voiture.

Un Humain a une quantité d'argent disponible sur son compte en banque. Un Humain peut acheter des voitures. Un Humain peut faire des trajets avec une voiture pour un certain nombre de kilomètres. Avant de commencer un trajet, l'humain vérifie s'il a assez d'essence, sinon, il va à la station service faire le plein. Lorsqu'un humain fait le plein de sa voiture, son compte en banque diminue.

  • Ecrivez une classe Humain et une classe Voiture compatibles avec cette description.
  • Instanciez ensuite Humain et Voiture afin de tester des scenarios d'utilisation.