Believemy logo purple

Le guide complet sur les classes en Python

Une classe est utilisée en Python pour créer un modèle qui sert à créer des objets : on parle de programmation orientée objet.
Believemy logo

En Python, le mot-clé class permet de définir ce que l’on appelle une classe, c’est-à-dire une structure qui modélise un concept ou un objet du monde réel.

Les classes sont un pilier fondamental de la programmation orientée objet (ce qu'on abrège POO), un paradigme largement utilisé dans les projets Python de moyenne et grande envergure.

Créer une classe revient à définir un modèle générique qui pourra ensuite être instancié plusieurs fois. Ces instances sont appelées objets.

Une classe, c'est comme un plan d’architecte. L’objet c'est la maison qu’on construit à partir de ce plan.

 

Définition d'une classe

Une classe est une abstraction. Elle regroupe des données (appelées attributs) et des comportements (appelés méthodes) dans une même structure logique.

Par exemple, une classe Voiture pourrait regrouper :

  • Des attributs : marque, modèle, couleur ;
  • Des méthodes : demarrer(), accelerer(), freiner().

La classe permet donc de modéliser une entité de manière propre et réutilisable, comme nous l'avons vu.

Chaque fois qu’on crée un objet à partir d’une classe, on parle d’instance de cette classe. Ces objets peuvent interagir entre eux, être stockés dans des listes, modifiés, copiés, etc.

 

Syntaxe de base d'une classe

Voici la syntaxe minimale pour créer une classe en Python :

PYTHON
class Animal:
    def __init__(self, nom):
        self.nom = nom

    def parler(self):
        print(f"{self.nom} fait un bruit.")

Décryptons ce que fait cet exemple ! 😋

Dans un premier temps, nous avons le mot-clé class : il indique ) Python que nous sommes en train d'en créer une.

Ensuite, nous donnons son nom : Animal.

Une classe doit avoir son nom sous format PascalCase pour respecter les conventions.

Enfin, on utilise la méthode __init__() qui est appelée automatiquement à la création d'une instance. C'est ce qu'on appelle un constructeur.

À noter aussi que self fait référence à l'objet courant.

 

Créer une instance en Python

Une fois qu’une classe est définie, on peut en créer une instance (c’est-à-dire un objet unique basé sur ce modèle) :

PYTHON
# Ce qu'on avait déjà créé
class Animal:
    def __init__(self, nom):
        self.nom = nom

    def parler(self):
        print(f"{self.nom} fait un bruit.")

# Ici : on crée notre objet
mon_chien = Animal("Rex")
mon_chien.parler()  # Rex fait un bruit.

Ce qu'il se passe dans cet exemple :

  • Animal("Rex") appelle automatiquement la méthode __init__() ;
  • self.nom = nom stocke le nom dans l’objet ;
  • parler() utilise cet attribut pour afficher une phrase.

 

Attributs d'instance vs attributs de classe

Il est essentiel de bien distinguer les deux types d’attributs, à savoir les attributs d'instance et les attributs de classe.

Attributs d'instance

Ils sont spécifiques à chaque objet, définis via self.

PYTHON
class Voiture:
    def __init__(self, marque):
        self.marque = marque

Chaque voiture aura sa propre marque.

 

Attributs de classe

Ils sont partagés par toutes les instances (donc tous les objets) de la classe.

PYTHON
class Voiture:
    roues = 4  # attribut de classe

print(Voiture.roues)  # 4

Les attributs de classe sont utiles pour définir des constantes qui sont communes à toutes les instances.

 

Tableau comparatif

Attribut d'instanceAttribut de classe
Défini dans __init__() ou une méthodeDéfini directement dans la classe
Unique à chaque objetPartagé entre tous les objets
Accès par self.attributAccès par Classe.attribut ou self.attribut

 

Les méthodes d'une classe

Une méthode est une fonction définie à l’intérieur d’une classe.

Elle peut :

  • Accéder aux données de l’objet (self.attribut) ;
  • Modifier des valeurs internes ;
  • Effectuer une action liée à l’état de l’objet.
PYTHON
class Compte:
    def __init__(self, titulaire, solde):
        self.titulaire = titulaire
        self.solde = solde

    def deposer(self, montant):
        self.solde += montant
        print(f"{montant}€ déposés. Nouveau solde : {self.solde}€")

c = Compte("Alice", 100)
c.deposer(50)  # 50€ déposés. Nouveau solde : 150€

 Les méthodes rendent la classe dynamique et interactive. Elles permettent de faire vivre les objets. 👀

 

Le principe de l'encapsulation et la visibilité

L’encapsulation est un principe fondamental en programmation orientée objet. Elle consiste à cacher les détails internes d’un objet afin de ne pas exposer des données sensibles ou inutiles à l’extérieur de la classe.

De manière générale, Python ne gère pas la protection des données au sens littéral. Mais il existe des conventions pour voir immédiatement quand une donnée peut être utilisée ou non :

  • nom : accessible partout (public) ;
  • _nom : signalé comme protégé, usage interne recommandé ;
  • __nom : privé, nom "manglé" pour éviter les collisions.

Allez ! Prenons un petit exemple.

PYTHON
class Banque:
    def __init__(self, titulaire):
        self._solde = 0      # protégé
        self.__titulaire = titulaire  # privé

    def afficher_solde(self):
        return self._solde

Ici, même si _solde est techniquement accessible, il est conventionnellement réservé à un usage interne. Le double underscore __ empêche l’accès direct depuis l’extérieur (banque.__titulaire lève une erreur).

 

Les classes et l'héritage

L’héritage permet de créer une nouvelle classe à partir d’une classe existante. Cela permet de réutiliser et spécialiser des comportements communs.

C’est une manière puissante de réutiliser du code tout en personnalisant certains comportements.

Exemple simple d'héritage

PYTHON
class Animal:
    def parler(self):
        print("L'animal fait un bruit.")

class Chien(Animal):
    pass

rex = Chien()
rex.parler()  # Affiche : L'animal fait un bruit.

La classe Chien n’a pas sa propre méthode parler(), donc elle hérite directement de celle de Animal.

 

Redéfinir une méthode (overriding)

On peut modifier (ou "redéfinir") une méthode héritée pour qu’elle ait un comportement différent dans la classe enfant. C'est ce qu'on appelle l'overriding.

PYTHON
class Animal:
    def parler(self):
        print("L'animal fait un bruit.")

class Chien(Animal):
    def parler(self):
        print("Le chien aboie.")

rex = Chien()
rex.parler()  # Affiche : Le chien aboie.

Cette version remplace la méthode parler() de la classe Animal.

 

Utiliser super() pour appeler la méthode parente

Parfois, on veut ajouter un comportement tout en conservant celui de la classe parente. C’est là qu’intervient super() :

PYTHON
class Chat(Animal):
    def parler(self):
        super().parler()  # Appelle parler() d’Animal
        print("Le chat miaule.")

super() est très utile quand vous souhaitez enchaîner des appels à travers une hiérarchie de classes, notamment en héritage multiple.

 

Les classes imbriquées

En Python, on peut définir une classe à l’intérieur d’une autre. On parle alors de classe imbriquée.

Cela permet de regrouper des structures logiquement liées sous un même toit.

Voici un petit exemple avec une classe imbriquée :

PYTHON
class Voiture:
    def __init__(self, marque):
        self.marque = marque

    class Moteur:
        def __init__(self, type_moteur):
            self.type_moteur = type_moteur

Et on utilise la classe imbriquée ainsi :

PYTHON
moteur = Voiture.Moteur("hybride")
print(moteur.type_moteur)  # hybride

On peut par exemple utiliser une classe imbriquée quand la classe interne n'a pas de sens en dehors de la classe externe.

Évitez d'utiliser une classe imbriquée si vous avez besoin que cette dernière soit utilisée dans plusieurs contextes. De plus, utiliser trop de classes imbriquées peut parfois réduire fortement toute la lisibilité du projet. 😉

 

Les méthodes spéciales des classes

Les méthodes spéciales, aussi appelées "dunder methods 🇺🇸" (pour "double underscore"), sont des fonctions qui ont un nom encadré par deux underscores, comme __init__, __str__, etc.

Python les appelle automatiquement dans des contextes particuliers : création d'objet, impression, comparaison, etc.

Voici les méthodes plus fréquentes :

  • __init__() -> appelée lors de la création d’une instance ;
  • __str__() -> utilisée par print() pour afficher un objet ;
  • __repr__() -> utilisée dans l’interpréteur pour représenter l’objet ;
  • __eq__() -> comparaison avec == ;
  • __len__() -> permet d’utiliser len(obj).

Prenons cet exemple concret :

PYTHON
class Livre:
    def __init__(self, titre, auteur):
        self.titre = titre
        self.auteur = auteur

    def __str__(self):
        return f"{self.titre} écrit par {self.auteur}"

l = Livre("1984", "George Orwell")
print(l)  # 1984 écrit par George Orwell

Les méthodes spéciales permettent donc de rendre nos objets plus naturels à utiliser dans le langage Python.

 

Les méthodes de classe et les méthodes statiques

En Python, en plus des méthodes d’instance (qui utilisent self comme nous l'avons vu 😋), nous pouvons définir deux autres types de méthodes au sein d'une classe :

  • Les méthodes de classe (@classmethod) ;
  • Et les méthodes statiques (@staticmethod).

Les méthodes de classe

Elle reçoit la classe elle-même comme premier argument, nommé conventionnellement cls.

Cela permet notamment de créer des constructeurs alternatifs ou des fonctions qui concernent la classe dans son ensemble (et non une instance en particulier).

Exemple :

PYTHON
class Utilisateur:
    def __init__(self, nom):
        self.nom = nom

    @classmethod
    def creer_invite(cls):
        return cls("Invité")

invite = Utilisateur.creer_invite()
print(invite.nom)  # Invité

Ici, creer_invite est une méthode qui retourne une instance prédéfinie de la classe. Elle utilise cls pour construire l’objet, ce qui la rend compatible même avec des sous-classes.

Utilisez @classmethod quand vous avez besoin de construire ou manipuler une classe sans passer par une instance.

 

Les méthodes statiques

Elle ne prend aucun argument implicite. C’est simplement une fonction rangée dans une classe par souci d'organisation.

Exemple :

PYTHON
class Maths:
    @staticmethod
    def carre(x):
        return x * x

print(Maths.carre(5))  # 25

Dans cet exemple la méthode ne dépend ni d’un objet ni de la classe : c’est une fonction purement utilitaire, placée là pour regrouper la logique.

 

Tableau comparatif

Type de méthodeParamètre principalAccès à l'instance / classeUsage
InstanceselfInstanceLire / Modifier les attributs
ClasseclsClasseConstructeur alternatif (@classmethod)
StatiqueAucunAucunMéthodes utilitaires (@staticmethod)

 

L'héritage multiple sur les classes

L’héritage multiple signifie qu’une classe peut hériter de plusieurs classes parentes à la fois. Cela donne de la flexibilité, mais demande aussi un peu plus d’attention. 😅

PYTHON
class Parle:
    def parler(self):
        print("Je parle.")

class Bouge:
    def bouger(self):
        print("Je bouge.")

class Robot(Parle, Bouge):
    pass

r2d2 = Robot()
r2d2.parler()  # Je parle.
r2d2.bouger()  # Je bouge.

Ici, la classe Robot hérite des méthodes des deux classes, sans aucune redéfinition. C’est simple et efficace.

Si deux classes parentes définissent une méthode du même nom, Python suit un ordre strict pour décider laquelle appeler : c’est le MRO, ou ordre de résolution des méthodes.

Exemple de conflit :

PYTHON
class A:
    def afficher(self):
        print("A")

class B:
    def afficher(self):
        print("B")

class C(A, B):
    pass

c = C()
c.afficher()  # A

Python appelle la méthode afficher() de la classe la plus à gauche dans la déclaration (A ici).

Vous pouvez afficher cet ordre avec la méthode __mro__ :

PYTHON
print(C.__mro__)
# (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)

 

Composition vs héritage : quelle approche choisir ?

L’héritage n’est pas la seule manière de structurer un programme orienté objet.

Python propose aussi un autre principe fondamental : la composition, qui repose sur l'idée de "avoir un" au lieu de "être un" (c'est un peu tordu).

Qu'est-ce que la composition ?

La composition consiste à intégrer un objet dans un autre objet, plutôt qu’à hériter de sa classe.

Exemple :

PYTHON
class Moteur:
    def demarrer(self):
        print("Le moteur démarre.")

class Voiture:
    def __init__(self):
        self.moteur = Moteur()

    def rouler(self):
        self.moteur.demarrer()
        print("La voiture roule.")

Ici, la classe Voiture contient un objet de type Moteur. Elle ne l’étend pas, mais l’utilise pour accomplir sa tâche.

 

Composition vs héritage : que choisir ?

CritèreHéritageComposition
Relation logique"est un" (Un Chien est un Animal)"a un" (Une voiture a un moteur)
CouplageFortFaible
FlexibilitéMoindreÉlevée
RéutilisationPar héritagePar délégation

Utilisez l’héritage quand la relation est naturelle ("un carré est un rectangle")

Utilisez la composition quand vous assemblez des fonctionnalités ("une voiture a un GPS").

Facile, non ? 👀

 

Typage et annotations des classes

Python permet d’annoter les types des attributs et des paramètres pour plus de clarté, notamment avec l’aide d’outils comme mypy, pylance ou des IDE comme PyCharm.

Voici un exemple de typage dans une classe :

PYTHON
class Produit:
    nom: str
    prix: float

    def __init__(self, nom: str, prix: float):
        self.nom = nom
        self.prix = prix

Grâce à ces annotations, on sait immédiatement à quoi s’attendre et les outils de développement peuvent vérifier automatiquement la cohérence des types.

On précise par exemple :

  • Que nom est une chaîne de caractère (str) ;
  • Que prix est un flottant (float) - donc un nombre à virgule.

On peut également typer les méthodes :

PYTHON
def calculer_tva(self, taux: float) -> float:
    return self.prix * taux

Ici taux s'attend à recevoir également un flottant, calculer_tva doit retourner un flottant aussi (-> float) !

Finalement, les annotations ne changent pas le fonctionnement du programme (elles sont non obligatoires à l’exécution), mais elles renforcent la documentation et la maintenabilité.

 

Les dataclasses, une alternative pour les classes

Introduites en Python 3.7 via le module dataclasses, les dataclasses offrent une manière simple, propre et concise de créer des classes contenant surtout des données.

Créer une classe classique avec attributs, constructeur, méthodes spéciales (__repr__, __eq__, etc.) peut être verbeux.

Les dataclasses font tout cela automatiquement ! 👀

PYTHON
from dataclasses import dataclass

@dataclass
class Livre:
    titre: str
    auteur: str
    pages: int

livre1 = Livre("1984", "Orwell", 328)
print(livre1)
# Livre(titre='1984', auteur='Orwell', pages=328)

Pas besoin d’écrire __init__, __repr__ ou __eq__ : tout est généré automatiquement.

Mais ce n'est pas tout, il est possible d'ajouter des options pour rendre une classe immuable ou pour la trier automatiquement :

PYTHON
@dataclass(frozen=True, order=True)
class Produit:
    nom: str
    prix: float

Dans cet exemple :

  • frozen=True rend l’objet immuable (comme un tuple) ;
  • order=True permet de trier les objets (<, >, etc.).

 

Les classes et les exceptions

Créer nos propres exceptions personnalisées nous permet de contrôler finement les erreurs dans un code orienté objet, tout en gardant des messages clairs.

Voici un exemple d'exception :

PYTHON
class SoldeInsuffisantError(Exception):
    """Exception levée quand le solde est insuffisant."""
    pass

Et voici comment l'intégrer dans une classe :

PYTHON
class Compte:
    def __init__(self, solde):
        self.solde = solde

    def retirer(self, montant):
        if montant > self.solde:
            raise SoldeInsuffisantError("Fonds insuffisants.")
        self.solde -= montant

En créant nos propres exceptions, on facilite le débogage et la compréhension du code.

 

Questions fréquentes sur les classes

Quand on commence avec les classes (et même après !) on peut se poser de nombreuses questions... Essayons de voir les plus fréquentes d'entre-elles.

À quoi sert self dans une classe ?

self est une référence à l’instance courante d’un objet. Il permet d’accéder à ses attributs et d’appeler ses méthodes depuis l’intérieur de la classe.

 

Faut-il toujours utiliser __init__ ?

Pas toujours ! Si vous utilisez une @dataclass, Python crée __init__ pour vous.

Sinon, __init__ est indispensable pour initialiser les attributs.

 

Peut-on avoir plusieurs classes dans un même fichier ?

Oui, et c’est courant. Mais veillez à ce que le fichier reste lisible : une classe principale par fichier est une bonne pratique si le projet devient complexe.

 

Comment apprendre Python ?

Avec une formation complète.

Découvrez notre glossaire Python

Parcourez les termes et définitions les plus couramment utilisés dans le domaine du développement avec Python.