Mémo

Classe
Classe
Modèle (plan) décrivant les données (attributs) et les comportements (méthodes) d'un type d'objet.
Objet (instance)
Exemplaire concret créé à partir d'une classe.
Attribut
Variable associée à un objet, accessible avec objet.attribut.
Méthode
Fonction définie dans une classe, appelée avec objet.methode().
self
Référence à l'objet courant, premier paramètre de toute méthode.
Syntaxe d'une classe
class NomClasse:
    def __init__(self, param1, param2):
        self.attribut1 = param1
        self.attribut2 = param2

    def methode(self):
        return self.attribut1
  • __init__ est le constructeur : appelé automatiquement à la création ;
  • __str__ définit l'affichage de l'objet par print() ;
  • __repr__ définit la représentation dans la console ;
  • __eq__ définit l'égalité avec ==.
Encapsulation

On peut distinguer les attributs et méthodes :

  • publics : accessibles partout (convention : pas de préfixe) ;
  • privés (convention) : préfixés par _ (avertissement) ou __ (name mangling).

En pratique au programme de NSI, on utilise des accesseurs (get_) et des mutateurs (set_) pour contrôler l'accès aux attributs.

Pièges fréquents
  • Oublier self comme premier paramètre des méthodes ;
  • Confondre la classe et l'instance : Cercle est une classe, c = Cercle(5) est une instance ;
  • Oublier les parenthèses à l'instanciation : Cercle (la classe) $≠$ Cercle() (une instance) ;
  • Modifier un attribut de classe partagé par toutes les instances (variable de classe vs d'instance).

Exemples

Classe Cercle
import math

class Cercle:
    def __init__(self, rayon):
        self.rayon = rayon

    def aire(self):
        return math.pi * self.rayon ** 2

    def perimetre(self):
        return 2 * math.pi * self.rayon

    def __str__(self):
        return f"Cercle de rayon {self.rayon}"

    def __eq__(self, other):
        return self.rayon == other.rayon

c1 = Cercle(5)
c2 = Cercle(3)
print(c1)              # Cercle de rayon 5
print(c1.aire())       # 78.539...
print(c1 == c2)        # False
print(c1 == Cercle(5)) # True (grâce à __eq__)
Classe CompteBancaire (encapsulation)
class CompteBancaire:
    def __init__(self, titulaire, solde=0):
        self._titulaire = titulaire
        self._solde = solde

    def get_solde(self):
        return self._solde

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

    def retirer(self, montant):
        if 0 < montant <= self._solde:
            self._solde -= montant
            return True
        return False

    def __str__(self):
        return f"Compte de {self._titulaire} : {self._solde} €"
Classe Carte (pour un jeu de cartes)
class Carte:
    def __init__(self, valeur, couleur):
        self.valeur = valeur     # "As", "2", ..., "Roi"
        self.couleur = couleur   # "Pique", "Coeur", ...

    def __str__(self):
        return f"{self.valeur} de {self.couleur}"

    def __repr__(self):
        return f"Carte('{self.valeur}', '{self.couleur}')"

Exercices

Exercice 1 — Classe Rectangle
3 pts

Créer une classe Rectangle avec :

  • Un constructeur prenant la largeur et la hauteur ;
  • Une méthode aire() ;
  • Une méthode perimetre() ;
  • Une méthode est_carre() renvoyant True si le rectangle est un carré ;
  • Une méthode __str__.
Voir la solution
class Rectangle:
    def __init__(self, largeur, hauteur):
        self.largeur = largeur
        self.hauteur = hauteur

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

    def perimetre(self):
        return 2 * (self.largeur + self.hauteur)

    def est_carre(self):
        return self.largeur == self.hauteur

    def __str__(self):
        return f"Rectangle {self.largeur} × {self.hauteur}"

Vérification :

r = Rectangle(4, 6)
print(r.aire())       # 24
print(r.perimetre())  # 20
print(r.est_carre())  # False
c = Rectangle(5, 5)
print(c.est_carre())  # True
Exercice 2 — Classe Fraction
4 pts

Créer une classe Fraction représentant une fraction irréductible avec :

  • Un constructeur prenant numérateur et dénominateur, qui simplifie la fraction ;
  • Une méthode __str__ affichant par exemple "3/4" ;
  • Une méthode __add__(self, other) pour additionner deux fractions ;
  • Une méthode __eq__(self, other) pour tester l'égalité.

On pourra utiliser math.gcd pour le PGCD.

Voir la solution
import math

class Fraction:
    def __init__(self, num, den):
        if den == 0:
            raise ValueError("Dénominateur nul")
        if den < 0:     # signe au numérateur
            num, den = -num, -den
        g = math.gcd(abs(num), den)
        self.num = num // g
        self.den = den // g

    def __str__(self):
        if self.den == 1:
            return str(self.num)
        return f"{self.num}/{self.den}"

    def __add__(self, other):
        return Fraction(
            self.num * other.den + other.num * self.den,
            self.den * other.den
        )

    def __eq__(self, other):
        return self.num == other.num and self.den == other.den

Vérification :

a = Fraction(2, 4)    # simplifié en 1/2
b = Fraction(1, 3)
print(a)              # 1/2
print(a + b)          # 5/6
print(a == Fraction(3, 6))  # True
Exercice 3 — Classe Eleve
3 pts

Créer une classe Eleve avec :

  • Un constructeur prenant le nom et une liste de notes (vide par défaut) ;
  • Une méthode ajouter_note(note) qui ajoute une note entre 0 et 20 ;
  • Une méthode moyenne() ;
  • Une méthode meilleure_note() ;
  • Une méthode __str__ affichant le nom et la moyenne.

Attention : ne pas utiliser une liste mutable comme valeur par défaut !

Voir la solution
class Eleve:
    def __init__(self, nom, notes=None):
        self.nom = nom
        self.notes = notes if notes is not None else []

    def ajouter_note(self, note):
        if 0 <= note <= 20:
            self.notes.append(note)

    def moyenne(self):
        if len(self.notes) == 0:
            return 0
        return sum(self.notes) / len(self.notes)

    def meilleure_note(self):
        if len(self.notes) == 0:
            return None
        return max(self.notes)

    def __str__(self):
        return f"{self.nom} (moyenne : {self.moyenne():.1f})"

Piège évité : écrire def __init__(self, nom, notes=[]) partagerait la même liste entre toutes les instances créées sans argument.

On utilise None comme sentinelle et on crée une nouvelle liste dans le constructeur.