Classes et objets

Un objet est un bout de code auquel est associé :

Un objet, n'est donc pas isolé, il partage ses fonctionnalités avec tous les objets de sa classe.

Créer ses propres classes

De façon générale, on peut définir un objet et une classe comme :

Définition

Un objet est une structure de données (les champs de la structure de donnée sont appelés attributs) sur laquelle on peut effectuer des opérations (appelées méthodes).

Pour pouvoir facilement créer une structure particulière et donner un moyen simple d'effectuer les opérations sur celle-ci, on utilise des classes comme patron de ces objets :

  • elles vont gérer la création des objets via un constructeur.
  • elles contiennent dans leur espace de noms les différentes méthodes que l'on pourra appliquer

On crée une classe par rapport à un besoin que l'on veut satisfaire. Supposons que l'on veuille créer une classe compteur qui doit satisfaire les besoins suivant :

Le code python suivant est correct et utilise une classe compteur qui satisfait nos besoins :

from compteur import Compteur

c1 = Compteur()
c2 = Compteur()
c1.incrémente()
c2.incrémente()
c1.incrémente()

print(c1.valeur, c2.valeur)

Que fait le code précédent ? En particulier que va afficher l'instruction print ?

corrigé

A la lecture du code, on a donc envie que le code :

  1. crée de deux compteurs de valeur initiales 0,
  2. incrémente le compteur c1 deux fois et le compteur c2 une fois avec la méthode incrémente
  3. affiche à l'écran l'attribut valeur des compteurs c1 et c2. Cet attribut représente le nombre de fois où chaque compteur a été incrémenté, c'est à dire 2 pour c1 et 1 pour c2

Notez que l'on a pas eu besoin du code de Compteur() ni de incrémente() pour "voir" ce que faisait le code. C'est une de la puissance de la programmation objet :

À retenir

Un code dont les objets sont bien nommés doit pouvoir se lire et être interprétable sans connaître le corps des fonctions et méthodes utilisées.

Formalisation cette notion de besoin avec l'UML.

Modélisation UML

Pour représenter les différentes possibilités offertes par une classe on utilise l'Unified Modeling Language (UML)

Vous pouvez suivre ce petit tutoriel UML pour comprendre sa notation et son utilité.

L'UML peut être très compliqué. Nous allons uniquement l'utiliser ici comme une représentation synthétique d'une classe/objet. Vous le verrez dans les exemples ci-dessous mais, en gros, une classe en UML c'est le diagramme :

une classe UML

Définition

Un diagramme UML est une boite qui décrit une classe. Elle contient 4 informations :

  • <nom de la classe> qui sera un nom sans espace. En python ce nom sera avec une majuscule.
  • une case pour y placer les différents attributs. on précisera le type (entier, chaîne de caractères, une classe particulière d'objet, ...) de chaque attribut
  • le constructeur de la classe qui sera exécuté pour créer un objet. On le désigne en UML par le nom de la classe (en python, c'est __init__) et on en donnera sa signature complète.
  • une case pour toutes les méthodes dont on donnera sa signature complète.

Par exemple un point et un polygone qui les utilise :

point / polygone

Ou le classique personne et compte bancaire :

personne / compte

UML du Compteur

À retenir

Pour construire le modèle UML d'une classe, il faut se poser les deux questions suivantes :

  • "que dois-je pouvoir faire ? " ce qui permettra de déterminer les méthodes de la classe
  • "que dois-je stocker comme information pour que les méthodes fonctionnent ?" ce qui permettra de déterminer les attributs de chaque objet.

On va essayer de comprendre le code pour produire une représentation UML de la classe Compteur. L'analyse du besoin que l'on a effectué nous a permit de définir l'usage ue l'on veut faire du compteur :

Ce qui donne le diagramme UML du compteur :

compteur

À retenir

Pour créer un diagramme UML :

  1. on commence toujours par le nom de la classe
  2. on explicite ses méthodes, c'est à dire comment on va utiliser les objets (ici incrémenter un compteur).
  3. on crée la structure de données qui va permettre de stocker les informations nécessaires à son utilisation : ce sont les attributs (ici un entier pour stocker le nombre de fois où on l'a incrémenté).

Implémentation en python

La modélisation UML ne nous indique pas l'implémentation des différentes méthodes, il nous indique juste comment on peut les utiliser. Pour exécuter et utiliser notre compteur, il faut le coder.

N'hésitez pas à jeter un coup d'œil au tutoriel de python sur ses classes. Ce cours est là pour vous montrer tout ce qu'il y a dedans, à part (peut-être) la partie sur l'héritage et les itérateurs.

La modélisation UML se transcrit presque mot pour mot en python. En codant la classe suivante dans un fichier nommé compteur.py l'exemple va fonctionner :

class Compteur:
    def __init__(self):
        self.valeur = 0

    def incrémente(self):
        self.valeur = self.valeur + 1

On va détailler plus tard les méthodes et moyens de construire des classes en python mais de l'écriture de la classe compteur on peut d'ores et déjà en déduire que :

Classes en python

Définition de classes

La définition d'une classe est un bloc python :


class <nom de la classe>:
    def __init__(self, paramètre 1, ..., paramètre n):
        # création des attributs
        # initialisation de l'objet
    def méthode(self, paramètre 1, ..., paramètre n_1):
        # ...
        # instructions de la méthode
        # ...
    # autres méthodes

Ce bloc contient :

Notez qu'en python on ne décrit pas les différents attributs. On les créera dans le constructeur en utilisant le paramètre self qui est présent dans toutes les méthodes et contient l'objet appelant la méthode (on verra comment en détail ci-après).

À retenir

En python :

  • les attributs sont déclarés dans le constructeur,
  • __init__ n'a pas de return,
  • le constructeur et les méthodes ont toutes comme premier paramètre self qui contient l'objet appelant.

De façon formelle :

Définition

Une classe en python est un objet de type classe contenant un espace de nommage.

Reprenons l'exemple de la classe compteur et examinons son exécution. à la fin de la ligne 1, on vient d'importer le module compteur qui contient uniquement une définition de classe, on est dans le cas suivant :

L'espace de nommage de la classe Compteur contient deux méthodes (les fonctions définies dans une classes sont appelées méthodes) :

Les deux méthodes prennent comme premier paramètre un objet nommé self, qui est la manière explicite de python de montrer quel objet est utilisé lors de l'appel de méthodes :

Définition

le premier paramètre de toute méthode, noté self, est l'objet sur lequel on va appliquer la méthode (l'objet à gauche du . lors de l'appel à celle-ci par une notation pointée).

Vous pouvez appeler ce premier paramètre comme vous voulez, mais il est très très déconseillé de le faire car votre code en deviendra moins lisible (tout le monde utilise le nom self).

Création d'un objet

En python, le retour de l'exécution d'une classe (l'utilisation de la classe comme si c'était une fonction) produit un objet. Par exemple :

Certains objets se créent juste avec leur valeur comme les entiers, les réels ou encore les chaines de caractères. En python 3 est équivalent à int(3) par exemple.

Le code suivant :

o = MaClasse(paramètre_1, ..., paramètre_n)

Va créer un objet de type MaClasse en effectuant les différentes étapes suivantes :

  1. créant un objet vide o de type MaClasse contenant un espace de nommage dont le parent est l'espace de nommage de sa classe
  2. il exécute le constructeur __init__ sur l'objet : MaClasse.__init__(o, paramètre 1, ..., paramètre n) (c'est pour ça que la méthode __init__ n'a pas de retour)

En prenant l'exemple du compteur. l'exécution de la ligne 3 (c1 = Compteur()) se déroule comme suit :

  1. Création d'un nouvel objet :
  2. Exécution du constructeur __init__ avec le nouvel objet en paramètre. À la fon de l'exécution du constructeur on est dans la configuration suivante :

On refait pareil pour la ligne 4 (c2 = Compteur()), ce qui fait qu'on se trouve dans l'état suivant :

Comme le constructeur est toujours appliqué au nouvel objet créé chaque objet va bien avoir des attributs distincts !

À retenir

En python, les attributs d'un objet sont explicitement créés dans le constructeur.

Exécution de méthodes

L'exécution de méthodes se fait simplement en utilisant les règles des espaces de nommages et de la notation pointée.

  1. on cherche le nom (potentiellement récursivement) dans l'espace de nommage
  2. l'objet appelant est donné comme premier paramètre de la méthode.

Ainsi pour l'exemple du compteur, l'exécution de la ligne 5 (c1.incrémente()) se déroule comme suit :

  1. on cherche le nom incrémente dans l'espace de nommage de c1. Il n'y est pas
  2. on cherche alors le nom incrémente dans l'espace de nommage parent, c'est celui de sa classe et on le trouve !
  3. on exécute la méthode incrémente en plaçant l'objet appelant (ici c1) en premier paramètre de la méthode
  4. La méthode accède aux différents attributs de l'objet en utilisant la notation pointée ((ici self.valeur))

Le résultat de l'exécution de la ligne 5 est alors :

À vous pour vérifier que vous avez compris :

Donnez le schéma des espaces de nommages jusqu'après l'exécution de la ligne 7 de l'exemple du compteur.

corrigé

Détaillez l'exécution de la ligne 9 de l'exemple du compteur (print(c2.valeur)).

corrigé

  1. on cherche à exécuter la fonction print : il faut trouver l'objet qui est son premier paramètre
  2. son paramètre est l'objet associé au nom valeur dans l'espace de nommage de l'objet associé au nom c2
  3. le nom c2 existe dans l'espace des variables, c'est un objet de la classe Compteur
  4. L'espace de nommage de cet objet contient le nom valeur qui est associé à un entier valant 1 : c'est notre paramètre de la fonction print
  5. on affiche à l'écran un entier valant 1.

Code Python de la classe Compteur