Ce cours est mis à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International.
Structurer avec des classes (Bac 🎯)⚓︎
Sources et crédits pour ce cours
Pour préparer ce cours, j'ai utilisé :
- le cours de Gilles Lassus
- le cours de Franck Chambon
- le manuel NSI chez Ellipses de Balabonski, Conchon, Filliâtre, Nguyen
- le manuel NSI chez Hachette sous la direction de Michel Beaudouin Lafon
- le cours de mon collègue Pierre Duclosson
🔖 Synthèse de ce qu'il faut retenir pour le bac
Un problème⚓︎
Problème
Dans un programme Python, on veut :
- créer des vecteurs du plan à partir de leurs coordonnées
- les manipuler à l'aide d'opérations classiques : calculer la norme d'un vecteur, additionner deux vecteurs, multiplier un vecteur par un scalaire.
Paradigme procédural : une solution avec des fonctions⚓︎
Solution avec variables et fonctions
A partir des connaissances de première, on peut définir :
- une structure de données : un vecteur est un
tuple
de deux entiers. On choisit le typetuple
plutôt quelist
car les coordonnées d'un vecteur ne doivent pas changer normalement, on dit qu'un vecteur est une donnée immuable - des fonctions pour manipuler cette structure de données, chacune renvoie un nouveau vecteur résultat de l'opération souhaitée
Cette façon de programmer est tout à fait convenable pour un petit script mais imaginons que ce code s'inscrit dans un projet plus grand où l'on manipule des temps qu'on doit aussi ajouter :
# Tests
(insensible à la casse)(Ctrl+I)
Plusieurs problèmes se posent lorsqu'on manipule plusieurs structures de données dans un gros programme. On peut citer entre autres :
- Le problème du cloisonnement des espaces de nommage : dans l'exemple on voit qu'il faudrait distinguer les noms des fonctions d'addition sinon la dernière déclarée va écraser la précédente. Une solution1 serait d'ajouter des suffixes :
addition_temps
ouaddition_vecteur
mais c'est lourd. On aimerait disposer pour un même programme d'une façon de définir un espace de nommage distinct regroupant variables et fonctions pour une même structure de données. - Le problème de l'organisation du code : pour que le code soit lisible il faudrait regrouper les définitions des structures de données avec les déclarations des fonctions qui les manipulent. On aimerait disposer d'une syntaxe Python pour organiser nos structures de données et définir nos propres types de données personnalisés comme les types
list
outuple
de Python.
Paradigme Objet : une solution avec des classes⚓︎
Solution avec classe, attributs et méthode
Le paradigme de Programmation Orientée Objet (POO) permet de répondre de façon élégante aux problèmes de cloisonnement des espaces de nommage et d'organisation du code. Un paradigme est une façon de programmer. En POO on manipule des objets qui interagissent entre eux.
- Un objet représente une structure données par un exemple un vecteur du plan
- Deux objets qui représentent la même structure de données appartiennent à la même classe, par exemple la classe
Vecteur
- Un objet possède un certain nombre d'attributs qui le caractérisent : par un exemple un objet de la classe
Vecteur
possède deux attributsx
ety
pour chaque coordonnée - Les attributs d'un objet sont définis lors de sa création par le constructeur de sa classe
- Un objet peut posséder aussi un certain nombre de méthodes qui permettent de le manipuler : par exemple une méthode
addition
permet d'additionner un vecteur à un autre.
On donne une traduction dans le paradigme de Programmation Orientée Objet (POO) de la solution précédente écrite dans le paradigme procédural utilisé jusqu'à présent dans nos programmes.
On peut établir l'analogie suivante :
Paradigme procédural | Paradigme objet |
---|---|
Variable | Attribut |
Fonction | Méthode |
# Tests
(insensible à la casse)(Ctrl+I)
Certains points techniques seront explicités plus loin, mais on peut déjà remarquer que :
- le mot clef
class
permet de définir une classe. - un objet est créé avec la syntaxe
Nom_classe(paramètres)
par exemplew1 = Vecteur(10, -4)
out1 = Temps(1, 59, 45)
- une action sur un objet est appliquée à l'aide d'une méthode en utilisant la notation pointée
objet.methode(paramètres)
, par exemplew3 = w1.addition(w2)
out3 = t1.addition(t2)
. - dans notre exemple, on définit deux classes :
- chacune contient un constructeur
__init__
permettant d'initialiser les attributs d'un objet - l'objet créé est désigné par
self
- chaque classe contient une méthode
addition
permettant d'additionner un autre objet de même classe à l'objet courant désigné parself
. __init__
etaddition
sont des noms partagés par les deux classesVecteur
etTemps
, chacune définit donc un espace de nommage distinct.
- chacune contient un constructeur
Un peu d'histoire⚓︎
Exercice 1
🎯 Histoire de l'informatique
Cet exercice est un QCM. Vous devez cocher la seule réponse correcte par question. Vous pouvez faire des recherches sur le Web pour répondre.
-
Question 1 : Qui est considéré comme le père de la programmation orientée objet ?
- Alan Turing
- John McCarthy
- Dennis Ritchie
- Alan Kay
-
Question 2 : Quel langage de programmation a introduit pour la première fois le concept de programmation orientée objet ?
- C
- Java
- Smalltalk
- Python
-
Question 3 : Quelle décennie est celle du triomphe de la programmation orientée objet ?
- 1960
- 1970
- 1980
- 1990
-
Question 1 : Qui est considéré comme le père de la programmation orientée objet ?
- ❌ Alan Turing
- ❌ John McCarthy
- ❌ Dennis Ritchie
- ✅ Alan Kay
-
Question 2 : Quel langage de programmation a introduit pour la première fois le concept de programmation orientée objet ?
- ❌ C
- ❌ Java
- ✅ Smalltalk
- ❌ Python
-
Question 3 : Quelle décennie est celle du triomphe de la programmation orientée objet ?
- ❌ 1960
- ❌ 1970
- ❌ 1980
- ✅ 1990
Des objets partout en Python⚓︎
Exercice 2
Vous avez déjà manipulé des objets
La syntaxe objet.methode(paramètres)
a déjà été utilisée en Première avec les types de données list
ou str
de Python. Les données de type list
sont en fait des objets de la classe list
. Toutes les données de Python sont des objets; les types de base sont des classes.
>>> type([])
<class 'list'>
>>> type('')
<class 'str'>
>>> type(0)
<class 'int'>
>>> type(0.0)
<class 'float'>
>>> type(True)
<class 'bool'>
Question 1
Dresser un tableau d'état de la variable ls
ligne par ligne lors de l'exécution du programme ci-dessous :
🐍 Script Python | |
---|---|
1 2 3 4 5 6 |
|
🐍 Script Python | |
---|---|
1 2 3 4 5 6 |
|
On peut afficher la liste des méthodes disponibles pour un objet avec dir(objet)
.
>>> type([])
<class 'list'>
>>> dir([])
['__add__', '__class__', '__class_getitem__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__MAX_SIZEof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
Notez que même les fonctions comme dir
ou len
sont en fait équivalentes à des méthodes de l'objet, même si c'est un peu particulier pour les types prédéfinis de Python écrits en C
(c'est une fonction écrite en C
qui est appelée pour la fonction len
plutôt que la méthode __len__
).
>>> a = [1, 4]
>>> len(a)
2
>>> a.__len__()
2
Question 2
Écrire une fonction de signature uncapitalize(chaine:str)->str
qui prend en paramètre une chaîne de caractères et renvoie la chaîne avec les mêmes caractères tous en majuscules sauf le premier.
⚠️ : contrainte : ne pas utiliser de boucle, seulement les méthodes de la classe str
>>> uncapitalize("explicit i better than implicit")
'eXPLICIT I BETTER THAN IMPLICIT'
🐍 Script Python | |
---|---|
1 2 |
|
On peut afficher la liste des méthodes disponibles pour un objet de type str
avec dir('')
.
>>> dir('')
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__MAX_SIZEof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'removeprefix', 'removesuffix', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
-
Une autre serait de définir chaque structure de données dans des programmes différents. Python permet alors d'importer ces modules dans le programme client. Par exemple des fonctions
randint
différentes existent dans le modulerandom
et dans le modulenumpy
, on peut les utiliser dans le même programme en préfixant du nom du module :random.randint
etnumpy.randint
. Mais il est naturel d'avoir envie de regrouper dans un même module des structures de données qui traitent du même thème, par exemple les différents types de personnage d'un jeu. ↩
# Tests
(insensible à la casse)(Ctrl+I)