> Comprendre la classe Python et la POO : class, __init__, attributs, méthodes, self, héritage, encapsulation, méthodes spéciales et @property, par l'exemple.

*Source : https://coder-studio.com/tutoriels/classes-python-poo/*

---

[// Python](https://coder-studio.com/tutoriels/)

# Les classes en Python : la POO expliquée par l'exemple

Par **Kévin Papot** · publié le 8 novembre 2025

Une **classe Python** est un modèle qui décrit un type d’objet : ses données (attributs) et ses comportements (méthodes). C’est le cœur de la programmation orientée objet (POO), un paradigme qui aide à organiser un programme autour d’entités cohérentes. Ce guide explique par l’exemple tout ce qu’il faut savoir : `class`, `__init__`, attributs et méthodes, `self`, héritage, encapsulation, méthodes spéciales et `@property`.

## Définir une classe avec class

On déclare une classe avec le mot-clé `class` et une majuscule en tête du nom (convention `PascalCase`).

```
class Chien:
    pass

rex = Chien()   # on crée une instance
print(type(rex))   # <class '__main__.Chien'>
```

Une classe est un plan ; une **instance** est un objet concret construit à partir de ce plan. On peut créer autant d’instances qu’on veut.

## Le constructeur **init**

La méthode spéciale `__init__` s’exécute automatiquement à la création de l’objet. Elle sert à initialiser les attributs.

```
class Chien:
    def __init__(self, nom, age):
        self.nom = nom
        self.age = age

rex = Chien("Rex", 3)
print(rex.nom)   # Rex
print(rex.age)   # 3
```

Les valeurs passées à `Chien("Rex", 3)` sont reçues par `__init__` et stockées dans l’objet.

## Le rôle de self

`self` représente l’instance courante. C’est toujours le **premier** paramètre des méthodes, et Python le transmet automatiquement lors de l’appel. Grâce à lui, chaque objet garde ses propres données.

```
a = Chien("Rex", 3)
b = Chien("Bella", 5)
print(a.nom, b.nom)   # Rex Bella (chaque instance a son nom)
```

Vous n’écrivez jamais `self` à l’appel : `a.nom` suffit, Python passe `a` comme `self` en coulisses.

## Attributs et méthodes

Un **attribut** est une donnée liée à l’objet ; une **méthode** est une fonction définie dans la classe.

```
class Chien:
    espece = "Canis familiaris"   # attribut de classe (partagé)

    def __init__(self, nom, age):
        self.nom = nom            # attribut d'instance
        self.age = age

    def aboyer(self):             # méthode
        return f"{self.nom} fait Ouaf !"

    def vieillir(self):
        self.age += 1

rex = Chien("Rex", 3)
print(rex.aboyer())   # Rex fait Ouaf !
rex.vieillir()
print(rex.age)        # 4
```

L’attribut de classe `espece` est commun à toutes les instances, tandis que `nom` et `age` sont propres à chacune. Les méthodes accèdent et modifient les attributs via `self`, un peu comme les [fonctions Python](https://coder-studio.com/tutoriels/fonctions-python/) manipulent leurs arguments.

## L’héritage

L’héritage permet de créer une classe spécialisée à partir d’une classe existante, en réutilisant son code.

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

    def parler(self):
        return "..."

class Chat(Animal):            # Chat hérite d'Animal
    def parler(self):          # on redéfinit (override) la méthode
        return "Miaou"

class Chien(Animal):
    def parler(self):
        return "Ouaf"

for animal in [Chat("Felix"), Chien("Rex")]:
    print(animal.nom, ":", animal.parler())
# Felix : Miaou
# Rex : Ouaf
```

La classe fille hérite des attributs et méthodes de la classe mère, et peut les **redéfinir**. Ce mécanisme, combiné au fait qu’un même appel (`parler`) produit un comportement différent selon le type, illustre le **polymorphisme**.

### Appeler la classe mère avec super()

Pour étendre — plutôt que remplacer — le comportement du parent, on utilise `super()`.

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

class Chien(Animal):
    def __init__(self, nom, race):
        super().__init__(nom)   # appelle Animal.__init__
        self.race = race

rex = Chien("Rex", "Berger")
print(rex.nom, rex.race)   # Rex Berger
```

`super().__init__(nom)` réutilise l’initialisation du parent, ce qui évite de dupliquer le code.

## L’encapsulation

L’encapsulation consiste à protéger l’accès direct aux données. En Python, la convention est le préfixe underscore.

```
class CompteBancaire:
    def __init__(self, solde):
        self._solde = solde         # "protégé" par convention

    def deposer(self, montant):
        if montant > 0:
            self._solde += montant

    def get_solde(self):
        return self._solde

compte = CompteBancaire(100)
compte.deposer(50)
print(compte.get_solde())   # 150
```

Un seul underscore (`_solde`) signale « usage interne, ne pas toucher directement ». Un double underscore (`__solde`) déclenche le _name mangling_, qui rend l’attribut plus difficile d’accès depuis l’extérieur.

## Les méthodes spéciales (dunder)

Les méthodes entourées de doubles underscores (« dunder ») personnalisent le comportement des objets. `__str__` définit l’affichage lisible.

```
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return f"Point({self.x}, {self.y})"

    def __eq__(self, autre):
        return self.x == autre.x and self.y == autre.y

    def __add__(self, autre):
        return Point(self.x + autre.x, self.y + autre.y)

p = Point(1, 2)
print(p)                 # Point(1, 2)  (grâce à __str__)
print(Point(1, 2) == p)  # True         (grâce à __eq__)
print(p + Point(3, 4))   # Point(4, 6)  (grâce à __add__)
```

Il en existe beaucoup : `__len__`, `__repr__`, `__getitem__`… Elles permettent à vos objets de se comporter comme des types natifs.

## Le décorateur @property

`@property` transforme une méthode en attribut « calculé », accessible sans parenthèses, tout en gardant le contrôle.

```
class Rectangle:
    def __init__(self, largeur, hauteur):
        self.largeur = largeur
        self.hauteur = hauteur

    @property
    def aire(self):
        return self.largeur * self.hauteur

r = Rectangle(4, 5)
print(r.aire)   # 20 (sans parenthèses !)
```

On peut aussi contrôler l’écriture avec un _setter_ et valider les valeurs.

```
class Temperature:
    def __init__(self, celsius):
        self._celsius = celsius

    @property
    def fahrenheit(self):
        return self._celsius * 9 / 5 + 32

    @fahrenheit.setter
    def fahrenheit(self, valeur):
        self._celsius = (valeur - 32) * 5 / 9

t = Temperature(25)
print(t.fahrenheit)   # 77.0
t.fahrenheit = 212
print(t._celsius)     # 100.0
```

`@property` est idéal pour exposer une valeur dérivée ou pour ajouter une validation sans changer l’interface de la classe.

## Conclusion

Vous avez fait le tour de la POO en Python : définir une `class`, initialiser avec `__init__`, comprendre `self`, manipuler attributs et méthodes, réutiliser du code par héritage et `super()`, protéger les données avec l’encapsulation, personnaliser le comportement avec les méthodes spéciales et exposer des valeurs calculées via `@property`. Le meilleur moyen de progresser reste de modéliser vos propres objets (un panier, un joueur, une facture).

### À lire ensuite

-   [Les fonctions en Python](https://coder-studio.com/tutoriels/fonctions-python/)
-   [Les dictionnaires en Python](https://coder-studio.com/tutoriels/dictionnaires-python/)
-   [Apprendre Python en 2026](https://coder-studio.com/tutoriels/apprendre-python/)
