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 permettent d'exécuter le code suivant :
from compteur import Compteur
c1 = Compteur()
c2 = Compteur()
c1.incrémente()
c2.incrémente()
c1.incrémente()
print(c2.valeur)
Pour construire cette cette classe, il faut se poser la question :
- "que dois-je pouvoir faire avec mon compteur ? " 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.
Analyse du besoin
Ici notre besoin c'est de faire marcher le bout de code d'une façon plausible. Il faut se demander ce que veut l'utilisateur de ce code python.
Le programme commence par importer le mot Compteur d'un module nommé compteur (donc placé dans un fichier nommé compteur.py dans le dossier du projet) et on l'exécute 2 fois pour l'affecter à 2 noms différents. Pour voir ce que peut être Compteur, plusieurs indices :
- cela ne doit pas être une fonction normale, sinon
c1etc2seraient identiques. - le mot
Compteurà une majuscule, ce qui correspond (par convention) en python à des noms de classes
A la lecture du code, on a donc envie que le code :
- création de deux compteurs
- en incrémente un deux fois et l'autre qu'une seule fois
- affiche à l'écran la valeur d'un des compteurs (celui qui a été incrémenté une fois) qu'on suppose égale à 1
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.
Les fonctionnalités que notre compteur doit avoir sont :
- ajouter une unité à un compteur
- connaître la valeur du compteur.
Pour que l'on puisse avoir plusieurs compteurs (si on n'a qu'un seul compteur, ce n'est pas la peine de faire des objets), il faut que chaque compteur ait une valeur à lui : valeur est un attribut. En revanche, on veut pouvoir incrémenter tous les compteurs : incrémente() est une méthode.
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 :

- pour chaque attribut on pourra préciser le type (entier, chaîne de caractères, une classe particulière d'objet, ...) si c'est important
- pour chaque méthode on donnera sa signature complète (son nom et ses paramètres) pour que l'on puisse l'utiliser.
- le constructeur de la classe sera désigné par le nom de la classe
On peut même combiner les diagramme UML ensemble. Par exemple un point et un polygone :

Ou le classique personne et compte bancaire :

UML du Compteur
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__ - 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 contenant deux parties :
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
- La première partie est constituée du constructeur nommé
__init__ - La seconde parties est constituées des différentes méthodes
En python, toutes les méthodes sont des fonctions définies dans le bloc classe :
- le constructeur d'une classe sera toujours la méthode :
__init__. C'est une méthode spéciale. - le 1er paramètre de chaque méthode est toujours
self. A l'exécution, python donnera à ce paramètre l'objet qui appelle la méthode, on ne le voit pas lorsque l'on écrit le code.
La méthode __init__ n'a pas de return, mais elle est utilisée dans le processus de création d'un objet.
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.