Cours Python · Module 3: Programmation orientée objet

Les classes comme espèces dans la nature

10 min de lecture
Dans cette leçon9

Bienvenue dans le module 3 ! Darwin ici, avec un nouveau paradigme de programmation.

Jusqu'à présent, tu as appris la programmation procédurale - tu écrivais des fonctions qui traitent des données. Il est maintenant temps de découvrir la programmation orientée objet (POO) - une façon d'organiser le code autour d'objets qui combinent données et comportements.

Imagine la classification biologique. Les biologistes ne décrivent pas chaque animal à partir de zéro. À la place, ils créent des espèces aux caractéristiques partagées :

  • Tous les lions (Panthera leo) ont quatre pattes, sont carnivores et vivent en groupes
  • Tous les pythons royaux (Python regius) sont des serpents, avalent leur proie entière et chassent la nuit

En POO, on fait la même chose - on crée des classes comme « modèles » d'objets !

Qu'est-ce que la programmation orientée objet ?

La POO est un paradigme de programmation fondé sur le concept d'objets qui contiennent :

  • Des données (attributs, propriétés) - elles décrivent l'état de l'objet
  • Des comportements (méthodes) - ils décrivent ce que l'objet peut faire

Concepts clés de la POO :

  1. Classe - un modèle, une recette, une espèce biologique
  2. Objet - une instance concrète d'une classe, un animal concret
  3. Attributs - les données de l'objet (caractéristiques de l'animal)
  4. Méthodes - fonctions associées à l'objet (comportements de l'animal)
1# Exemple simple - classification biologique
2
3class Species:
4    """Classe représentant une espèce"""
5
6    def __init__(self, name, habitat, diet):
7        """Constructeur - initialise l'objet"""
8        self.name = name
9        self.habitat = habitat
10        self.diet = diet
11
12    def describe(self):
13        """Méthode - décrit l'espèce"""
14        return f"{self.name} - habitat : {self.habitat}, régime : {self.diet}"
15
16# Création d'objets (instances de la classe)
17lion = Species("Lion", "savane", "viande")
18python = Species("Python", "jungle", "rongeurs")
19
20# Utilisation des objets
21print(lion.describe())   # "Lion - habitat : savane, régime : viande"
22print(python.describe()) # "Python - habitat : jungle, régime : rongeurs"

Cet exemple contient deux nouveaux éléments. __init__ est le constructeur : Python l'appelle automatiquement à la création d'un objet, par ex. Species("Lion", "savane", "viande"). self est une référence à l'objet courant, cet animal précis : self.name = name y enregistre le nom. Nous les verrons plus en détail dans la prochaine leçon.

Pourquoi la POO ?

Sans POO (procédural)

Commençons par le style que tu connais déjà : chaque caractéristique d'un animal est une variable séparée, et la fonction les reçoit toutes en arguments.

1# Les données et les fonctions sont séparées
2lion_name = "Lion"
3lion_habitat = "savane"
4lion_diet = "viande"
5
6python_name = "Python"
7python_habitat = "jungle"
8python_diet = "rongeurs"
9
10def describe_animal(name, habitat, diet):
11    return f"{name} - habitat : {habitat}, régime : {diet}"
12
13print(describe_animal(lion_name, lion_habitat, lion_diet))
14print(describe_animal(python_name, python_habitat, python_diet))

Avec deux animaux, ça fonctionne encore, mais chaque nouvel animal ajoute trois variables à suivre. Avec vingt espèces, on mélange vite les noms et on passe à la fonction l'habitat du lion avec le régime du python.

Avec la POO

Voici les deux mêmes animaux écrits sous forme d'objets : la classe Animal garde les données et la méthode describe() au même endroit.

1# Les données et les fonctions sont réunies dans des objets
2class Animal:
3    def __init__(self, name, habitat, diet):
4        self.name = name
5        self.habitat = habitat
6        self.diet = diet
7
8    def describe(self):
9        return f"{self.name} - habitat : {self.habitat}, régime : {self.diet}"
10
11lion = Animal("Lion", "savane", "viande")
12python = Animal("Python", "jungle", "rongeurs")
13
14print(lion.describe())
15print(python.describe())

Le résultat est le même, mais le code est mieux organisé : les données et les comportements sont réunis, et chaque nouvel animal tient en une ligne Animal(...) au lieu de trois nouvelles variables.

4 piliers de la POO

1. Encapsulation

Regrouper les données et les méthodes dans un seul objet + masquer les détails d'implémentation.

1class BankAccount:
2    def __init__(self, balance):
3        self.__balance = balance  # Attribut privé (__)
4
5    def deposit(self, amount):
6        """Méthode publique"""
7        if amount > 0:
8            self.__balance += amount
9
10    def get_balance(self):
11        """Accès public à l'attribut privé"""
12        return self.__balance
13
14account = BankAccount(1000)
15account.deposit(500)
16print(account.get_balance())  # 1500

Le solde ne change qu'à travers la méthode deposit(), qui n'accepte que des montants positifs. Essayer de lire account.__balance depuis l'extérieur de la classe lève une AttributeError, car un attribut avec deux underscores est privé.

2. Abstraction

Masquer la complexité, ne montrer que l'interface nécessaire.

1class Car:
2    def start_engine(self):
3        """Interface simple"""
4        self.__check_fuel()
5        self.__ignite_spark_plugs()
6        self.__start_motor()
7        print("Moteur démarré !")
8
9    def __check_fuel(self):
10        """Implémentation cachée"""
11        pass
12
13    def __ignite_spark_plugs(self):
14        """Implémentation cachée"""
15        pass
16
17    def __start_motor(self):
18        """Implémentation cachée"""
19        pass
20
21car = Car()
22car.start_engine()  # Interface simple - pas besoin de connaître les détails !

3. Héritage

Créer de nouvelles classes à partir de classes existantes - une hiérarchie taxonomique !

1class Animal:
2    """Classe de base (parent, superclasse)"""
3    def __init__(self, name):
4        self.name = name
5
6    def eat(self):
7        return f"{self.name} mange"
8
9class Mammal(Animal):
10    """Classe dérivée (enfant, sous-classe)"""
11    def __init__(self, name, fur_color):
12        super().__init__(name)  # Appelle le constructeur de la classe de base
13        self.fur_color = fur_color
14
15    def nurse_young(self):
16        return f"{self.name} allaite ses petits"
17
18lion = Mammal("Lion", "doré")
19print(lion.eat())          # Hérité de Animal
20print(lion.nurse_young())  # Méthode propre à Mammal

4. Polymorphisme

Des objets différents peuvent répondre à la même méthode de façons différentes.

1class Dog:
2    def speak(self):
3        return "Ouaf ouaf !"
4
5class Cat:
6    def speak(self):
7        return "Miaou !"
8
9class Cow:
10    def speak(self):
11        return "Meuh !"
12
13# Polymorphisme - même méthode, comportements différents
14animals = [Dog(), Cat(), Cow()]
15for animal in animals:
16    print(animal.speak())  # Chacun « parle » différemment !

Exemple Safari - Classification des espèces

Réunissons ces éléments dans une seule classe : une espèce a un nom scientifique, un habitat et une liste d'observations, et ses méthodes comptent les individus et évaluent si l'espèce est menacée.

1class Species:
2    """
3    Classe représentant une espèce dans l'expédition Safari
4
5    Analogie : une espèce biologique aux caractéristiques partagées
6    """
7
8    def __init__(self, scientific_name, common_name, habitat, dangerous=False):
9        """
10        Constructeur - initialise une nouvelle espèce
11
12        Args:
13            scientific_name: Nom scientifique (par ex. "Panthera leo")
14            common_name: Nom courant (par ex. "Lion")
15            habitat: Environnement naturel
16            dangerous: Dangereux pour l'humain ou non
17        """
18        self.scientific_name = scientific_name
19        self.common_name = common_name
20        self.habitat = habitat
21        self.dangerous = dangerous
22        self.observations = []  # Liste des observations
23
24    def add_observation(self, location, count, notes=""):
25        """Ajoute une observation d'espèce"""
26        observation = {
27            "location": location,
28            "count": count,
29            "notes": notes
30        }
31        self.observations.append(observation)
32        print(f"Observation ajoutée : {count}x {self.common_name} à {location}")
33
34    def get_total_observed(self):
35        """Renvoie le nombre total d'individus observés"""
36        return sum(obs["count"] for obs in self.observations)
37
38    def is_threatened(self):
39        """Vérifie si l'espèce est menacée (moins de 10 observations)"""
40        return self.get_total_observed() < 10
41
42    def get_status(self):
43        """Renvoie le statut de l'espèce"""
44        total = self.get_total_observed()
45        if total == 0:
46            return "Non observée"
47        elif self.is_threatened():
48            return f"Menacée ({total} individus)"
49        else:
50            return f"Stable ({total} individus)"
51
52    def describe(self):
53        """Description complète de l'espèce"""
54        danger_status = "DANGEREUSE" if self.dangerous else "Sûre"
55        return f"""
56╔═══════════════════════════════════════════════╗
57  {self.common_name} ({self.scientific_name})
58  Habitat : {self.habitat}
59  Statut : {danger_status}
60  Observations : {len(self.observations)}
61  Individus : {self.get_total_observed()}
62  État : {self.get_status()}
63╚═══════════════════════════════════════════════╝
64        """.strip()
65
66# Utilisation
67lion = Species(
68    scientific_name="Panthera leo",
69    common_name="Lion",
70    habitat="savane",
71    dangerous=True
72)
73
74# Ajout d'observations
75lion.add_observation("Savane du Nord", 5, "Troupe de chasse")
76lion.add_observation("Vallée du Rift", 3, "Deux adultes avec des petits")
77lion.add_observation("Parc du Serengeti", 8)
78
79# Affichage des informations
80print(lion.describe())
81print(f"\nTotal observé : {lion.get_total_observed()} lions")
82print(f"Est-elle menacée ? {'Oui' if lion.is_threatened() else 'Non'}")

Comparaison : fonctions ou classes

Approche procédurale (fonctions et dictionnaires)

D'abord, une espèce sous forme de simple dictionnaire et trois fonctions séparées qui créent, modifient et résument ce dictionnaire.

1def create_species(name, habitat):
2    """Renvoie un dictionnaire représentant une espèce"""
3    return {
4        "name": name,
5        "habitat": habitat,
6        "observations": []
7    }
8
9def add_observation(species_dict, location, count):
10    """Modifie le dictionnaire"""
11    species_dict["observations"].append({"location": location, "count": count})
12
13def get_total(species_dict):
14    """Calcule à partir du dictionnaire"""
15    return sum(obs["count"] for obs in species_dict["observations"])
16
17# Utilisation - les fonctions et les données sont séparées
18lion = create_species("Lion", "savane")
19add_observation(lion, "Nord", 5)
20print(get_total(lion))

Approche orientée objet

La même logique sous forme de classe : les clés du dictionnaire deviennent des attributs de l'objet, et les fonctions deviennent ses méthodes.

1class Species:
2    def __init__(self, name, habitat):
3        self.name = name
4        self.habitat = habitat
5        self.observations = []
6
7    def add_observation(self, location, count):
8        """Données et logique réunies !"""
9        self.observations.append({"location": location, "count": count})
10
11    def get_total(self):
12        return sum(obs["count"] for obs in self.observations)
13
14# Utilisation - données et comportements réunis
15lion = Species("Lion", "savane")
16lion.add_observation("Nord", 5)
17print(lion.get_total())

Avantages de la POO :

  • Données et méthodes réunies (regroupement logique)
  • Gestion de l'état plus facile
  • Possibilité d'hériter et de réutiliser le code
  • Meilleure conception pour les systèmes complexes
  • L'encapsulation protège les données

Quand utiliser la POO ?

Utilise la POO quand :

  • Tu modélises des entités du monde réel (animaux, véhicules, utilisateurs)
  • Tu as besoin de nombreux objets similaires avec des données différentes
  • Tu veux masquer les détails d'implémentation (encapsulation)
  • Tu as besoin de hiérarchies et d'héritage
  • Tu conçois une API ou une bibliothèque

Utilise des fonctions/procédures quand :

  • Le script est simple et linéaire
  • Tu traites des données sans état
  • Ce sont des fonctions utilitaires (outils d'aide)
  • Tu n'as pas besoin de plusieurs instances

Exemple pratique - Gestionnaire d'expédition

Pour finir, une classe plus grande qui gère toute une expédition : elle enregistre les membres de l'équipe et les découvertes de chaque jour, puis affiche un rapport à la fin.

1class Expedition:
2    """
3    Classe gérant une expédition Safari
4
5    Combine des données (lieu, équipe, découvertes) et des comportements (ajout, rapports)
6    """
7
8    def __init__(self, name, leader, start_date):
9        self.name = name
10        self.leader = leader
11        self.start_date = start_date
12        self.team_members = []
13        self.discovered_species = []
14        self.days_elapsed = 0
15
16    def add_team_member(self, member_name, role):
17        """Ajoute un membre de l'équipe"""
18        member = {"name": member_name, "role": role}
19        self.team_members.append(member)
20        print(f"{member_name} ({role}) a rejoint l'expédition")
21
22    def discover_species(self, species_name, location, count=1):
23        """Enregistre la découverte d'une espèce"""
24        discovery = {
25            "species": species_name,
26            "location": location,
27            "count": count,
28            "day": self.days_elapsed
29        }
30        self.discovered_species.append(discovery)
31        print(f"Jour {self.days_elapsed} : Découvert {count}x {species_name} à {location}")
32
33    def advance_day(self):
34        """Jour suivant de l'expédition"""
35        self.days_elapsed += 1
36        print(f"\nJour {self.days_elapsed} de l'expédition '{self.name}'")
37
38    def get_statistics(self):
39        """Statistiques de l'expédition"""
40        unique_species = len(set(d["species"] for d in self.discovered_species))
41        total_animals = sum(d["count"] for d in self.discovered_species)
42
43        return {
44            "expedition": self.name,
45            "leader": self.leader,
46            "days": self.days_elapsed,
47            "team": len(self.team_members),
48            "unique_species": unique_species,
49            "total_individuals": total_animals
50        }
51
52    def generate_report(self):
53        """Génère le rapport de l'expédition"""
54        stats = self.get_statistics()
55
56        report = f"""
57╔══════════════════════════════════════════════════════════╗
58  RAPPORT D'EXPÉDITION : {self.name}
59╠══════════════════════════════════════════════════════════╣
60  Chef : {self.leader}
61  Date de début : {self.start_date}
62  Jours sur le terrain : {stats['days']}
63  Membres de l'équipe : {stats['team']}
64╠══════════════════════════════════════════════════════════╣
65  DÉCOUVERTES :
66  - Espèces uniques : {stats['unique_species']}
67  - Total d'individus : {stats['total_individuals']}
68╠══════════════════════════════════════════════════════════╣
69  ÉQUIPE :
70        """
71
72        for member in self.team_members:
73            report += f"\n  - {member['name']} ({member['role']})"
74
75        report += "\n╚══════════════════════════════════════════════════════════╝"
76        return report
77
78# Simulation d'expédition
79expedition = Expedition("Safari 2024", "Dr Jane Wilson", "2024-06-01")
80
81# Ajout de l'équipe
82expedition.add_team_member("Darwin Brown", "Biologiste")
83expedition.add_team_member("Alex Chen", "Photographe")
84expedition.add_team_member("Maya Patel", "Guide")
85
86# Jours d'expédition
87expedition.advance_day()
88expedition.discover_species("Panthera leo", "Savane du Nord", 5)
89expedition.discover_species("Loxodonta africana", "Vallée des éléphants", 12)
90
91expedition.advance_day()
92expedition.discover_species("Python regius", "Jungle", 2)
93expedition.discover_species("Panthera leo", "Savane du Sud", 3)
94
95expedition.advance_day()
96expedition.discover_species("Gorilla gorilla", "Forêt de nuages", 8)
97
98# Rapport final
99print("\n" + expedition.generate_report())

Résumé

Dans cette leçon, tu as appris :

  • Ce qu'est la programmation orientée objet (POO)
  • La différence entre l'approche procédurale et l'approche orientée objet
  • Les 4 piliers de la POO : encapsulation, abstraction, héritage, polymorphisme
  • Pourquoi la POO est puissante pour organiser le code
  • Quand utiliser la POO et quand utiliser des fonctions
  • Les concepts de base : classe, objet, attribut, méthode
  • Des exemples Safari concrets

Point de contrôle

Avant de passer à la suite :

  • Tu comprends la différence entre une classe et un objet
  • Tu connais les 4 piliers de la POO (encapsulation, abstraction, héritage, polymorphisme)
  • Tu comprends pourquoi on regroupe données et comportements
  • Tu vois l'analogie entre les classes et les espèces biologiques
  • Tu sais quand utiliser la POO et quand utiliser des fonctions

Analogie Safari : une classe est une espèce (Panthera leo), un objet est un animal concret (un lion concret nommé Simba) !

Dans la prochaine leçon, Darwin t'apprendra comment créer tes propres classes et objets - tu construiras un système complet de classification des espèces !

Tu as repéré une erreur dans cette leçon ?

Teste tes connaissances

Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.

  1. 1. En Python, une classe est :

  2. 2. La méthode __init__ sert à :

Voici 2 des 4 questions de cette leçon. Réponds aux autres dans le jeu.

Tâches pratiques dans le jeu

  • Tri vertical

    Range les étapes de création d'une classe et d'un objet :

  • Éditeur de code

    Crée une classe Animal avec une méthode __init__ qui accepte name et species et les enregistre dans self.name et self.species

  • Éditeur de code

    Définis une classe Animal avec __init__(self, name, species) et crée une instance : tiger = Animal('Tiger', 'Panthera tigris')

  • Tri horizontal

    Range l'en-tête de la définition de la classe Animal :

  • Clique dans l'ordre

    Clique sur les éléments de l'en-tête d'un constructeur qui prend un paramètre name :

  • Tri vertical

    Range les lignes d'une classe Animal avec un constructeur (première méthode) et une méthode describe() :

Articles utiles