Classes et objets en Python
Définir une classe Python, manipuler attributs et méthodes, puis distinguer objets indépendants, alias et résultats des appels.
Environ 40 minutes
- Écrire une classe et instancier des objets
- Accéder aux attributs et appeler des méthodes
- Suivre une mutation et reconnaître un partage de référence
Deux noms de variables désignent-ils toujours deux objets différents ?
Un programme peut gérer plusieurs compteurs, chacun avec sa propre valeur et les mêmes opérations. Une classe regroupe la définition de ces données et de ces actions. Mais créer un deuxième nom ne crée pas forcément un deuxième compteur. Tu vas suivre les objets réellement modifiés et préciser les résultats des appels. Prérequis : fonctions, affectation, paramètres et listes Python. L’héritage et le polymorphisme ne font pas partie du noyau étudié ici.
1. Une classe décrit des objets
Une classe définit un type d’objets avec des attributs et des méthodes. Un objet créé à partir de cette classe est une instance. Les attributs mémorisent son état ; les méthodes sont les opérations définies dans la classe. Pour modéliser un compteur, on choisit ici un attribut valeur et deux méthodes, ajouter et lire. Ce modèle laisse de côté les propriétés inutiles au problème. Un compteur de places et un compteur de passages peuvent partager cette structure sans représenter le même objet réel. La pertinence d’une classe vient d’abord des opérations et des données nécessaires.
Le mot-clé class introduit la définition. La méthode spéciale __init__ initialise une instance quand l’appel de la classe crée cet objet. Dans une méthode d’instance ordinaire, le premier paramètre est appelé self par convention : il désigne l’instance sur laquelle la méthode agit. L’expression self.valeur accède donc à l’attribut de cette instance. Le nom depart est seulement un paramètre de l’appel d’initialisation ; il ne devient pas automatiquement un attribut. L’affectation self.valeur = depart rend cette donnée accessible dans les autres méthodes.
Exemple résolu
Définis Compteur, puis crée deux compteurs de valeurs initiales 4 et 9.
- Étape 1 : Définis la classe avec un retrait de quatre espaces par niveau.class Compteur:
def __init__(self, depart):
self.valeur = depart
def ajouter(self, quantite):
self.valeur = self.valeur + quantite
def lire(self):
return self.valeur - Étape 2 : Crée deux instances distinctes.a = Compteur(4)
b = Compteur(9) - Étape 3 : Lis a.valeur et b.valeur : les résultats attendus sont respectivement 4 et 9. Le code de la méthode est commun, mais les états appartiennent aux instances.
Résultat : a et b désignent deux objets distincts. La classe décrit leur fonctionnement ; chacun mémorise sa propre valeur.
Piège à éviter : Une affectation depart = 4 dans une fonction ne remplace pas self.valeur : le paramètre local et l’attribut d’instance n’ont pas le même rôle.
À toi d'essayer
Dans __init__(self, nom), on écrit seulement nom = nom. Pourquoi une méthode ne pourra-t-elle pas ensuite lire self.nom ?
Voir la réponseMasquer la réponse
L’affectation ne crée aucun attribut : elle réaffecte le paramètre local à sa propre valeur. Il faut écrire self.nom = nom pour mémoriser la donnée sur l’instance.
2. Appeler une méthode et suivre son effet
L’appel a.ajouter(3) applique la méthode ajouter à l’objet désigné par a. Python transmet cet objet comme premier argument de la méthode : dans son corps, self désigne a. L’argument explicite 3 correspond au paramètre quantite. Il n’est donc pas nécessaire d’écrire a.ajouter(a, 3). L’expression a.lire désigne la méthode accessible sur a ; a.lire() l’appelle et fournit son résultat. Les parenthèses distinguent l’accès à l’opération et son exécution. Cette distinction est fréquente dans les erreurs de début de programmation objet.
Modifier un objet et renvoyer une valeur sont deux actions différentes. Notre méthode ajouter change self.valeur mais ne contient pas return. Son résultat est donc None, tandis que lire renvoie la valeur sans la modifier. On peut employer une méthode pour son effet, pour son résultat, ou pour les deux si l’interface le prévoit. Il faut lire le contrat. L’instruction a = a.ajouter(3) est incorrecte pour conserver l’accès au compteur : l’objet est bien modifié, puis le nom a est réaffecté au résultat None.
Exemple résolu
Avec a = Compteur(4), que produisent r = a.ajouter(3) puis s = a.lire() ?
- Étape 1 : Dans ajouter, self.valeur vaut d’abord 4. L’addition avec 3 produit 7, affecté au même attribut.
- Étape 2 : La méthode se termine sans return : r reçoit None. Le compteur existe toujours et a le désigne encore.
- Étape 3 : La méthode lire renvoie self.valeur : s reçoit 7, sans nouvelle modification.
Résultat : Après les deux appels, a.valeur et s valent 7, et r vaut None. L’effet sur l’objet ne doit pas être confondu avec la valeur affectée à r.
Piège à éviter : Attendre r = 7 alors qu’ajouter ne contient aucun return revient à supposer un résultat absent de son interface.
Les méthodes peuvent préserver une propriété de l’état. Par exemple, si un compteur ne doit jamais devenir négatif, son initialisation et toutes ses opérations doivent vérifier les conditions nécessaires. Un contrôle dans une seule méthode ne suffit pas si d’autres chemins modifient l’attribut. En Python, un attribut public reste accessible depuis l’extérieur. L’encapsulation consiste surtout ici à définir une interface cohérente et à respecter cette interface. Un préfixe particulier n’est pas une barrière de sécurité absolue ; l’étude des mécanismes avancés de protection n’est pas nécessaire pour écrire une classe simple correcte.
À toi d'essayer
Le programme écrit a = Compteur(5), puis a = a.ajouter(2). Quelle est l’erreur et comment conserver l’accès au compteur ?
Correction détaillée pas à pasDans ce chapitre : 2 corrections détaillées.
3. Un nom peut partager un objet
L’affectation b = a ne recopie pas l’objet. Elle associe b à la même référence. On parle d’alias lorsque plusieurs noms permettent d’atteindre le même objet. Si l’objet est mutable, une modification faite par un nom est observable par l’autre. Cela ne signifie pas qu’une variable surveille l’autre : les deux noms conduisent au même état. En revanche, réaffecter un nom à un nouvel objet ne change pas l’objet désigné par les autres noms. Pour raisonner correctement, dessine les noms séparément des objets et modifie seulement les flèches lors d’une réaffectation.
Exemple résolu
Trace a = Compteur(4), b = a, b.ajouter(3), puis b = Compteur(10).
- Étape 1 : Le premier appel crée un compteur de valeur 4. L’affectation b = a donne deux noms pour ce seul objet.
- Étape 2 : La méthode appelée par b modifie sa valeur en 7. À cet instant, a.lire() et b.lire() renvoient tous deux 7.
- Étape 3 : Le dernier appel crée un second objet de valeur 10 et réaffecte seulement b. a continue de désigner le premier compteur.
Résultat : L’état final est a.lire() = 7 et b.lire() = 10. La mutation a changé un objet ; la dernière affectation a changé le lien d’un nom vers un objet.
Piège à éviter : Dire que b = Compteur(10) remet aussi a à 10 confond partage d’un objet et liaison permanente entre deux variables.
Les données mutables demandent une attention supplémentaire. Si chaque instance doit posséder sa propre liste, crée cette liste dans __init__, avec self.elements = []. Une liste placée comme attribut de classe peut être partagée entre les instances, et un paramètre par défaut mutable peut aussi être réutilisé entre les appels. Pour éviter cette difficulté dans les premières classes, initialise explicitement les conteneurs de chaque instance. Un test avec un seul objet ne détecte pas les partages involontaires ; crée toujours au moins deux instances et ne modifie que l’une.
À toi d'essayer
Une fonction reçoit un compteur c et appelle c.ajouter(1). Le programme appelant retrouve-t-il une valeur modifiée ? Et si la fonction fait seulement c = Compteur(0) ?
Correction détaillée pas à pasDans ce chapitre : 2 corrections détaillées.
4. Au bac : tester une classe
Au bac : passer du contrat aux vérifications
- Identifie les attributs d’instance et leurs valeurs initiales. Vérifie la présence de self pour les accès à ces attributs.
- Pour chaque méthode, distingue paramètres, résultat et modification éventuelle de l’état.
- Trace plusieurs appels en notant les états avant et après. Ajoute un cas avec deux instances indépendantes.
- Ajoute un cas avec deux noms pour le même objet et un cas de réaffectation d’un seul nom.
- Explique les résultats attendus avant l’exécution, puis utilise les écarts pour localiser la première opération incorrecte.
Une bonne série de tests ne se limite pas au résultat affiché à la fin. Vérifie l’initialisation, l’absence de modification lors d’une lecture et la conservation des états indépendants. Si l’interface impose une condition sur les paramètres, teste aussi cette condition ou respecte explicitement la précondition. La classe ne rend pas un programme correct par sa seule présence : elle aide à rassembler un état et les opérations qui le manipulent. Il reste à choisir une représentation, à spécifier les méthodes et à justifier que les propriétés utiles sont conservées.
Vocabulaire
- Instance :
- Objet particulier créé à partir d’une classe.
- Attribut d’instance :
- Donnée accessible sur un objet et participant à son état.
- Méthode :
- Opération définie dans une classe ; une méthode d’instance agit avec une référence à l’instance.
- Alias :
- Nom supplémentaire permettant d’atteindre le même objet.
Trois opérations à distinguer
| Écriture | Effet |
|---|---|
| b = a | Un nom supplémentaire pour la même référence. |
| b.ajouter(3) | Modification de l’objet atteint par b. |
| b = Compteur(10) | Création d’un objet et réaffectation de b. |
S’entraîner : ouvre l’espace Exercices du site.
À retenir
- Une classe définit un type ; une instance possède un état particulier.
- Une méthode d’instance reçoit implicitement l’objet lors d’un appel avec la notation pointée.
- Une méthode sans return renvoie None, même lorsqu’elle modifie l’objet.
- Une affectation de référence ne constitue pas une copie de l’objet.
Fiche de révision
Dessine les noms à gauche et les objets à droite : une affectation déplace une flèche, une mutation change un état.
Pour deux compteurs séparés, effectue deux appels de la classe.
Avant d’affecter le résultat d’une méthode, vérifie qu’elle renvoie effectivement la donnée attendue.
Deux instances et une modification sur une seule révèlent les conteneurs partagés par erreur.
Questions fréquentes
self est-il un mot-clé ?
Non. C’est la convention de nommage du premier paramètre des méthodes d’instance ordinaires.
__init__ crée-t-il à lui seul l’objet ?
Il initialise l’instance lors de sa création. Le mécanisme complet de création n’est pas étudié ici.
Et maintenant ?
Tu as lu le cours. L’étape suivante tient en une action.
Relis le coursReprends l’essentiel avant de passer à la suite.Prolonge l’expérience !
Continue à explorer ce chapitre à travers de nouvelles activités, des défis et des expériences interactives. Avec Premium, l’apprentissage continue !
Ici, tu débloques aussi 2 corrections détaillées.
Parent ? Suivez les progrès de votre enfant.
