Lesson 6/9 10 min

Classes and objects

Define a class with class, the __init__ constructor, self, attributes and methods, create instances, and __str__ for readable output.

FR EN

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.
  • self désigne l'objet courant ; il est passé automatiquement, jamais à la main.
  • Les attributs (self.x) sont les données ; les méthodes (fonctions avec self) sont les comportements.
  • __str__(self) renvoie une chaîne lisible utilisée par print().

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.
  • self refers to the current object; it's passed automatically, never by hand.
  • Attributes (self.x) are the data; methods (functions with self) are the behaviors.
  • __str__(self) returns a readable string used by print().
Avec l'IA

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.
Ré-explique sans regarder

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 ?

Une bonne explication dit : chaque appel de 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.
Accepter ou rejeter le code de l'IA

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'] ?!
À rejeter : piège classique de l'argument par défaut mutable. La liste [] 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.
Rappel libre

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.
À quoi sert la méthode __init__ dans une classe ?
Que représente self dans une méthode ?
À quoi sert __str__ ?
Next step

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 →
Besoin d'un développeur pour votre projet ?

Réponse sous 24h · Sans engagement