Cours de Python / Comprendre les classes, les instances et self
Une classe Python réunit des données et les opérations qui les utilisent ; une instance est un objet individuel créé à partir de cette définition. Cette organisation est utile lorsque plusieurs valeurs et leurs règles évoluent ensemble, sans imposer une classe à chaque calcul simple.
Relier une classe à ses instances
La classe Expense ci-dessous définit une dépense avec un nom, un montant et une méthode qui prépare sa description. Deux appels distincts à Expense produisent ici deux objets : une dépense de déjeuner et une dépense de bus. La définition des méthodes est commune, mais chaque objet conserve ses propres attributs name et amount.
Un attribut est une valeur accessible par un nom rattaché à un objet, comme first.amount. Une méthode est une fonction que l’on appelle à travers l’objet, comme first.describe(). Dans sa définition, self reçoit l’instance concernée. Ce nom est une convention ; le mécanisme ne dépend pas de son orthographe, mais conserver self rend le code reconnaissable.
__init__ prépare l’état d’une instance après sa création. Avec Expense("lunch", 8000), Python fournit l’instance à self, puis les deux valeurs aux paramètres name et amount. Vous ne passez pas self vous-même. La distinction entre création et initialisation est décrite dans le modèle de données de Python.
Créer deux dépenses et afficher leur description
La bibliothèque standard suffit : aucun paquet supplémentaire n’est nécessaire. Dans un dossier d’exercice, enregistrez le programme complet en UTF-8 sous classes_demo.py. Ouvrez un terminal dans ce dossier, sélectionnez l’environnement virtuel du projet et exécutez python classes_demo.py. Si votre installation macOS ou Ubuntu utilise python3 hors environnement virtuel, lancez python3 classes_demo.py.
class Expense:
def __init__(self, name, amount):
if amount < 0:
raise ValueError("negative amount")
self.name = name
self.amount = amount
def describe(self):
return f"{self.name}: {self.amount:,} KRW"
first = Expense("lunch", 8000)
second = Expense("bus", 2500)
print(first.describe())
print(second.describe())
print(first.amount + second.amount)
Le résultat attendu est :
lunch: 8,000 KRW
bus: 2,500 KRW
10500
Dans __init__, self.name = name et self.amount = amount stockent les arguments sur l’instance. La méthode describe renvoie une chaîne ; les appels print l’affichent. Le dernier print additionne les montants numériques, sans convertir les descriptions en nombres.
Les identifiants lunch et bus signifient ici « déjeuner » et « bus ». Le format {self.amount:,} groupe les chiffres par virgules : 8000 devient 8,000. Ce choix explicite est conservé dans le résultat de l’exercice ; ce n’est pas une localisation française automatique. KRW désigne le won sud-coréen et reste un libellé : aucune conversion monétaire n’est effectuée. Les options de regroupement figurent dans la syntaxe officielle des formats numériques.
Distinguer état indépendant et nom partagé
Pour cet exemple précis, la relation entre noms et objets se lit ainsi :
| Nom | Instance | Attributs initiaux |
|---|---|---|
| first | Objet A | name = "lunch", amount = 8000 |
| second | Objet B, distinct de A | name = "bus", amount = 2500 |
Ajoutez temporairement first.amount = 9000 après les deux créations, avant les print. La première description devient lunch: 9,000 KRW, la seconde reste bus: 2,500 KRW et le total devient 11500. Retirez cette affectation pour retrouver 10500.
En revanche, alias = first ne crée pas une autre dépense. alias et first désignent le même objet : modifier alias.amount change donc la valeur lue à travers first. Vérifiez alias is first, qui vaut True, et first is second, qui vaut False. Ce comportement prolonge les règles de portée des noms, alias et objets modifiables.
Pour cette classe, Expense.describe(first) et first.describe() renvoient la même description. Le premier appel transmet l’objet explicitement ; le second le fournit implicitement à self. Ne rajoutez pas first dans first.describe(first) : la méthode recevrait un argument de trop. Le tutoriel officiel sur les classes présente les instances et les appels de méthodes.
Vérifier les limites de la règle sur les montants
- Dans la première création, remplacez 8000 par -1. L’initialisation lève
ValueError: negative amountavant les affichages. Rétablissez ensuite 8000. - Essayez 0 : la dépense est acceptée, la première description affiche 0 KRW et le total vaut 2500. Rétablissez à nouveau 8000.
- Après les deux créations, ajoutez temporairement
first.amount = -1. Cette affectation directe ne repasse pas dans __init__ : le montant négatif est conservé. Retirez la ligne et vérifiez le résultat initial.
Le contrôle amount < 0 exprime seulement un rejet des valeurs négatives comparables. Il ne définit pas un contrat complet : une chaîne "8000" échoue lors de la comparaison, alors que True est accepté par ce contrôle. Si votre application exige des entiers hors booléens ou une unité précise, formulez cette règle et validez-la explicitement. Une vérification à la création ne protège pas toutes les modifications futures.
Avec cette classe ordinaire, une faute telle que first.amout = 9000 peut créer un nouvel attribut sans modifier amount. Inspectez le nom exact et vérifiez le résultat attendu au lieu de supposer que l’affectation a corrigé la dépense. Les annotations de type, les dataclasses et les tests peuvent clarifier une structure plus grande, mais ne garantissent pas à eux seuls toutes les règles métier à l’exécution.
Gardez enfin une responsabilité précise : représenter une dépense et ses règles n’oblige pas Expense à gérer aussi fichiers, réseau et affichage de toute l’application. Séparer les opérations externes facilite la réutilisation et le test des objets ; une fonction simple reste adaptée à un calcul sans état à conserver.




