Classes et objets
Un objet est un bout de code auquel est associé :
- des fonctionnalités (des méthodes) qui sont communes à tous les objets de sa classe
- des choses à lui tout seul (sa structure de donnée interne qui constitue ses attributs) qui lui permettent de se différentier des autres objets de sa classe même s'il a les mêmes fonctionnalités.
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 :
- créer un compteur de valeur initiale 0,
- pouvoir ajouter une unité à un compteur via une méthode,
- connaître la valeur du compteur.
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é
corrigé
A la lecture du code, on a donc envie que le code :
- crée de deux compteurs de valeur initiales 0,
- incrémente le compteur
c1deux fois et le compteurc2une fois avec la méthodeincrémente - affiche à l'écran l'attribut
valeurdes compteursc1etc2. Cet attribut représente le nombre de fois où chaque compteur a été incrémenté, c'est à dire 2 pourc1et 1 pourc2
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 :

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 :

Ou le classique personne et compte bancaire :

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 :
- ajouter une unité à un compteur via la méthode
incrémente - connaître la valeur du compteur via l'attribut
valeur
Ce qui donne le diagramme UML du compteur :

À retenir
Pour créer un diagramme UML :
- on commence toujours par le nom de la classe
- on explicite ses méthodes, c'est à dire comment on va utiliser les objets (ici incrémenter un compteur).
- 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 :
- une classe est un bloc python
- le constructeur est une fonction définie dans le bloc de classe et qui s'appelle
__init__(c'est une méthode spéciale !) - les attributs sont assignés dans l'espace de nom d'un objet nommé
selfqui est le premier paramètres de toutes les fonctions définies dans la classe - les méthodes sont des fonctions définies dans la classe
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 :
- le nom de la classe,
- le constructeur qui sera toujours la méthode :
__init__. C'est une méthode spéciale, - les méthodes.
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 dereturn,- le constructeur et les méthodes ont toutes comme premier paramètre
selfqui 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) :
__init__qui est le constructeur de la classeincrémentequi est une méthode
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 :
list(): crée un objet de typelist(une liste), sans paramètre.int(): crée un objet de typeint(un entier) sans paramètre (c'est 0).int(3.1415): crée un un objet de typeintavec un paramètre, valant le réel 3.1415 (c'est 3)float("3.1415"): crée un objet de typefloat(un réel) avec un paramètre valant la chaîne de caractères"3.1415".list(range(5)): crée un objet de typelistavec comme unique paramètre le résultat de la fonctionrange
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 :
- créant un objet vide
ode typeMaClassecontenant un espace de nommage dont le parent est l'espace de nommage de sa classe - 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 :
- Création d'un nouvel objet :

- 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.
- on cherche le nom (potentiellement récursivement) dans l'espace de nommage
- 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 :
- on cherche le nom
incrémentedans l'espace de nommage dec1. Il n'y est pas - on cherche alors le nom
incrémentedans l'espace de nommage parent, c'est celui de sa classe et on le trouve ! - on exécute la méthode
incrémenteen plaçant l'objet appelant (icic1) en premier paramètre de la méthode - 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é
corrigé

Détaillez l'exécution de la ligne 9 de l'exemple du compteur (print(c2.valeur)).
corrigé
corrigé
- on cherche à exécuter la fonction
print: il faut trouver l'objet qui est son premier paramètre - son paramètre est l'objet associé au nom
valeurdans l'espace de nommage de l'objet associé au nomc2 - le nom
c2existe dans l'espace des variables, c'est un objet de la classeCompteur - L'espace de nommage de cet objet contient le nom
valeurqui est associé à un entier valant 1 : c'est notre paramètre de la fonctionprint - on affiche à l'écran un entier valant 1.