Pourquoi une classe ?
Jusqu'ici, vos données et vos fonctions vivaient séparément : un dictionnaire pour le compte d'Alice d'un côté, une fonction deposer() de l'autre. Ça marche, mais dès que les objets se multiplient, on s'y perd. Une classe permet de regrouper au même endroit les données et les comportements qui vont ensemble.
L'image classique : la classe est un moule, et chaque objet créé à partir d'elle est un gâteau sorti de ce moule. Un seul moule, autant de gâteaux que vous voulez, chacun avec ses propres valeurs.
Note : cette leçon montre la POO côté Python (la syntaxe). Si vous voulez vraiment comprendre les concepts de la programmation orientée objet (encapsulation, héritage, polymorphisme), suivez le cours dédié à la POO, qui est agnostique du langage.
Définir une classe : class, __init__ et self
On définit une classe avec le mot-clé class. La méthode spéciale __init__ est le constructeur : Python l'appelle automatiquement à chaque création d'objet pour l'initialiser.
class Compte:
def __init__(self, titulaire, solde):
self.titulaire = titulaire
self.solde = solde
# On crée deux instances DISTINCTES à partir de la même classe
c = Compte("Alice", 100)
autre = Compte("Bob", 0)
print(c.titulaire) # Alice
print(c.solde) # 100
print(autre.solde) # 0
Le paramètre self représente l'objet en cours de manipulation : « mon propre titulaire », « mon propre solde », pas ceux du voisin. C'est lui qui rend chaque compte indépendant.
Note : vous écrivez Compte("Alice", 100) avec deux arguments, alors que __init__ en déclare trois (self, titulaire, solde). C'est normal : Python passe automatiquement l'objet lui-même en premier paramètre. Vous ne fournissez jamais self vous-même.
Attributs d'instance et méthodes
Les variables stockées sur l'objet (self.titulaire, self.solde) sont ses attributs. Les fonctions définies dans la classe sont ses méthodes : elles prennent toujours self en premier paramètre, ce qui leur donne accès à l'état de l'objet.
class Compte:
def __init__(self, titulaire, solde=0):
self.titulaire = titulaire
self.solde = solde
def deposer(self, montant):
self.solde += montant
def afficher_solde(self):
print(f"Solde de {self.titulaire} : {self.solde} euros")
c = Compte("Alice", 100)
c.deposer(50)
c.afficher_solde() # Solde de Alice : 150 euros
# Bob a son propre état, indépendant
bob = Compte("Bob")
bob.afficher_solde() # Solde de Bob : 0 euros
On appelle une méthode avec la notation pointée : c.deposer(50). Là encore, self est passé automatiquement : vous ne donnez que montant.
Bonne pratique : passez toujours par les méthodes pour modifier l'état (ici deposer()) plutôt que de toucher l'attribut directement. C'est le principe d'encapsulation : la classe garde le contrôle de ses propres données.
__str__ : un affichage lisible
Que se passe-t-il si on fait print() directement sur un objet ? Par défaut, Python affiche quelque chose d'illisible :
class Compte:
def __init__(self, titulaire, solde):
self.titulaire = titulaire
self.solde = solde
c = Compte("Alice", 150)
print(c) # <__main__.Compte object at 0x7f3a...>
Pour obtenir un affichage propre, on définit la méthode spéciale __str__. Elle doit renvoyer une chaîne, que print() utilisera automatiquement :
class Compte:
def __init__(self, titulaire, solde):
self.titulaire = titulaire
self.solde = solde
def __str__(self):
return f"Compte de {self.titulaire} : {self.solde} euros"
c = Compte("Alice", 150)
print(c) # Compte de Alice : 150 euros
Même principe que __init__ : c'est une méthode « magique » (encadrée de doubles soulignés) que Python appelle pour vous au bon moment, ici dès qu'on tente d'afficher l'objet ou de le convertir en texte avec str().
Récap
class Nom:définit le moule.__init__(self, ...)est le constructeur, appelé à chaque création d'objet.selfdésigne l'objet courant ; il est passé automatiquement, jamais à la main.- Les attributs (
self.x) sont les données ; les méthodes (fonctions avecself) sont les comportements. __str__(self)renvoie une chaîne lisible utilisée parprint().
Why a class?
So far, your data and your functions lived apart: a dictionary for Alice's account on one side, a deposit() function on the other. It works, but as soon as objects multiply, things get messy. A class lets you group together the data and the behaviors that belong together.
The classic image: the class is a mold, and each object built from it is a cake baked in that mold. One mold, as many cakes as you want, each with its own values.
Note: this lesson shows OOP from the Python side (the syntax). If you want to really understand the concepts of object-oriented programming (encapsulation, inheritance, polymorphism), follow the dedicated OOP course, which is language-agnostic.
Defining a class: class, __init__ and self
You define a class with the class keyword. The special __init__ method is the constructor: Python calls it automatically every time an object is created, to initialize it.
class Account:
def __init__(self, owner, balance):
self.owner = owner
self.balance = balance
# We create two DISTINCT instances from the same class
a = Account("Alice", 100)
other = Account("Bob", 0)
print(a.owner) # Alice
print(a.balance) # 100
print(other.balance) # 0
The self parameter refers to the object currently being handled: "my own owner", "my own balance", not the neighbor's. That's what makes each account independent.
Note: you write Account("Alice", 100) with two arguments, while __init__ declares three (self, owner, balance). That's normal: Python automatically passes the object itself as the first parameter. You never supply self yourself.
Instance attributes and methods
The variables stored on the object (self.owner, self.balance) are its attributes. The functions defined inside the class are its methods: they always take self as the first parameter, which gives them access to the object's state.
class Account:
def __init__(self, owner, balance=0):
self.owner = owner
self.balance = balance
def deposit(self, amount):
self.balance += amount
def show_balance(self):
print(f"{self.owner}'s balance: {self.balance} euros")
a = Account("Alice", 100)
a.deposit(50)
a.show_balance() # Alice's balance: 150 euros
# Bob has his own independent state
bob = Account("Bob")
bob.show_balance() # Bob's balance: 0 euros
You call a method with dotted notation: a.deposit(50). Here too, self is passed automatically: you only give amount.
Best practice: always go through methods to change state (here deposit()) rather than touching the attribute directly. That's the principle of encapsulation: the class keeps control over its own data.
__str__: readable output
What happens if you print() an object directly? By default, Python shows something unreadable:
class Account:
def __init__(self, owner, balance):
self.owner = owner
self.balance = balance
a = Account("Alice", 150)
print(a) # <__main__.Account object at 0x7f3a...>
To get clean output, define the special __str__ method. It must return a string, which print() will use automatically:
class Account:
def __init__(self, owner, balance):
self.owner = owner
self.balance = balance
def __str__(self):
return f"{self.owner}'s account: {self.balance} euros"
a = Account("Alice", 150)
print(a) # Alice's account: 150 euros
Same principle as __init__: it's a "magic" method (wrapped in double underscores) that Python calls for you at the right moment, here whenever you try to display the object or convert it to text with str().
Recap
class Name:defines the mold.__init__(self, ...)is the constructor, called every time an object is created.selfrefers to the current object; it's passed automatically, never by hand.- Attributes (
self.x) are the data; methods (functions withself) are the behaviors. __str__(self)returns a readable string used byprint().
Copiez ce prompt dans Claude ou ChatGPT :
Écris une classe Python Livre avec un constructeur __init__ (titre, auteur, pages), une méthode resume() qui affiche un résumé court, et une méthode __str__ pour un affichage lisible. Crée deux instances et montre que chacune a son propre état.
Sans relire le code de l'IA : avec tes mots, pourquoi deux instances créées avec Compte("Alice", 100) et Compte("Bob", 0) ont-elles chacune leur propre solde ? Quel rôle joue self là-dedans ?
Compte(...) crée un nouvel objet distinct, et __init__ écrit les attributs sur cet objet-là via self. self désigne l'instance en cours : self.solde = solde stocke le solde dans la mémoire propre d'Alice, pas dans une zone partagée. Du coup, modifier le solde de Bob ne touche jamais celui d'Alice : les deux self pointent vers deux objets différents.L'IA te propose cette classe Panier. Ton rôle de relecteur : l'accepter telle quelle ou la rejeter, et dire pourquoi.
class Panier:
def __init__(self, articles=[]):
self.articles = articles
def ajouter(self, article):
self.articles.append(article)
p1 = Panier()
p1.ajouter("pomme")
p2 = Panier()
print(p2.articles) # ['pomme'] ?!
[] de def __init__(self, articles=[]) est créée une seule fois, au moment où Python définit la fonction, puis partagée par toutes les instances qui ne fournissent pas d'articles. Résultat : p1 et p2 pointent vers la même liste, et ajouter une pomme à l'un la fait apparaître chez l'autre. Le correctif pro : def __init__(self, articles=None): puis self.articles = articles if articles is not None else [], pour créer une liste neuve à chaque objet.Sans remonter dans la leçon : à quoi sert __init__, et que doit renvoyer __str__ ?
__init__ est le constructeur : Python l'appelle automatiquement à chaque création d'objet pour l'initialiser, et c'est là qu'on pose les attributs sur self (ex. self.titulaire = titulaire). __str__ doit renvoyer une chaîne (pas l'afficher avec print) ; cette chaîne est celle que print(objet) et str(objet) utiliseront pour un affichage lisible.__init__ dans une classe ?self dans une méthode ?__str__ ?Your objects live in memory, but they vanish as soon as the program closes. To keep your data, we learn to read and write files, then to organize code into modules and install packages with pip.
Lesson 7: Files and modules →