248 lines
10 KiB
Markdown
248 lines
10 KiB
Markdown
# Fiche de révision : la programmation orientée objet
|
|
|
|
> Une fiche pour reprendre les bases, et surtout la différence entre une **classe** et un **objet**. Les exemples reprennent le TP Pokémon Arena.
|
|
|
|
---
|
|
|
|
## 1. Vous faites de la POO depuis la Première
|
|
|
|
Bonne nouvelle : la POO n'est pas un monde nouveau. Vous utilisez des objets depuis l'année dernière sans le savoir.
|
|
|
|
```python
|
|
>>> type(3)
|
|
<class 'int'>
|
|
>>> type("salut")
|
|
<class 'str'>
|
|
>>> type([1, 2])
|
|
<class 'list'>
|
|
```
|
|
|
|
Python vous le dit lui-même : `3` est un objet de la **classe** `int`, `"salut"` un objet de la classe `str`, `[1, 2]` un objet de la classe `list`.
|
|
|
|
Et quand vous écrivez `ma_liste.append(3)` ou `"salut".upper()`, vous appelez une **méthode** sur un **objet**. Le point entre les deux signifie : « applique cette méthode à cet objet-là ».
|
|
|
|
En Terminale, la seule nouveauté, c'est que vous allez **écrire vos propres classes**.
|
|
|
|
---
|
|
|
|
## 2. Classe ou objet ? La différence en une image
|
|
|
|
Reprenons l'image du cours : **la classe, c'est le plan d'architecte ; l'objet, c'est la maison construite.** Avec un seul plan, on peut construire toute une rue de maisons. Elles ont la même forme, mais chacune a sa propre couleur de volets, ses propres habitants, et si on repeint l'une, les autres ne changent pas.
|
|
|
|
Avec les Pokémon :
|
|
|
|
- la **classe** `Pokemon`, c'est le **formulaire vierge** du Pokédex : il prévoit des cases « nom », « type », « PV », mais il ne contient aucun Pokémon ;
|
|
- un **objet**, c'est un **Pokémon précis**, avec ses cases remplies : *ce* Pikachu-là, avec *ses* 80 PV.
|
|
|
|
| | Classe | Objet (ou instance) |
|
|
|:--|:--|:--|
|
|
| C'est… | un modèle, un plan | un exemplaire construit à partir du modèle |
|
|
| Combien ? | écrite **une seule fois** | créé **autant de fois qu'on veut** |
|
|
| Dans le code | `class Pokemon:` | `pikachu = Pokemon("Pikachu", "Electrik", 80)` |
|
|
| Nom | commence par une **majuscule** | une variable, en minuscules |
|
|
| Contient des valeurs ? | non, elle décrit **quelles** données existeront | oui, **ses propres** valeurs |
|
|
|
|
Voici une classe très courte :
|
|
|
|
```python
|
|
class Pokemon:
|
|
def __init__(self, nom, type_pokemon, pv):
|
|
self.nom = nom
|
|
self.type_pokemon = type_pokemon
|
|
self.pv = pv
|
|
|
|
def subir_degats(self, degats):
|
|
self.pv = self.pv - degats
|
|
```
|
|
|
|
Ce code **ne crée aucun Pokémon**. Il explique seulement comment en fabriquer un. Les objets naissent ici :
|
|
|
|
```python
|
|
pikachu = Pokemon("Pikachu", "Electrik", 80)
|
|
autre_pikachu = Pokemon("Pikachu", "Electrik", 80)
|
|
```
|
|
|
|
Deux objets, construits avec le même plan et les mêmes valeurs. Ce sont pourtant **deux Pokémon différents** :
|
|
|
|
```python
|
|
>>> autre_pikachu.subir_degats(30)
|
|
>>> pikachu.pv
|
|
80
|
|
>>> autre_pikachu.pv
|
|
50
|
|
```
|
|
|
|
Le premier Pikachu n'a rien senti. Chaque objet a **ses propres attributs**, rangés dans sa propre « boîte ».
|
|
|
|
!!! tip "À retenir"
|
|
|
|
**Un objet n'existe que lorsqu'on appelle la classe avec des parenthèses** : `Pokemon(...)`. Chaque appel fabrique un **nouvel** objet. Écrire `class Pokemon:` ne fabrique rien.
|
|
|
|
---
|
|
|
|
## 3. Le constructeur et `self`
|
|
|
|
Que se passe-t-il exactement quand on écrit `pikachu = Pokemon("Pikachu", "Electrik", 80)` ?
|
|
|
|
1. Python fabrique un **objet vide** de la classe `Pokemon`.
|
|
2. Il appelle automatiquement la méthode `__init__`, le **constructeur**, en lui donnant cet objet vide comme **premier paramètre**, `self`.
|
|
3. `__init__` remplit l'objet : `self.nom = nom` signifie « range la valeur `nom` dans la case `nom` de **cet objet-là** ».
|
|
4. L'objet rempli est rangé dans la variable `pikachu`.
|
|
|
|
`self` désigne donc toujours **l'objet sur lequel on est en train de travailler**. C'est la même chose pour les autres méthodes :
|
|
|
|
```python
|
|
pikachu.subir_degats(30)
|
|
```
|
|
|
|
est exactement équivalent à :
|
|
|
|
```python
|
|
Pokemon.subir_degats(pikachu, 30)
|
|
```
|
|
|
|
Python fait cette traduction tout seul : l'objet placé **avant le point** devient `self`. C'est pour cela que `self` apparaît dans la définition de la méthode, mais jamais entre les parenthèses à l'appel.
|
|
|
|
---
|
|
|
|
## 4. Le vocabulaire
|
|
|
|
| Mot | Sens | Dans le TP Pokémon |
|
|
|:--|:--|:--|
|
|
| **Classe** | le modèle | `Pokemon`, `Dresseur`, `Combat` |
|
|
| **Objet** ou **instance** | un exemplaire créé à partir de la classe | `pikachu`, `salameche`, `sacha` |
|
|
| **Instanciation** | le fait de créer un objet | `Pokemon("Pikachu", "Electrik", 80)` |
|
|
| **Attribut** | une donnée rangée dans l'objet | `pikachu.pv`, `pikachu.nom` |
|
|
| **Méthode** | une fonction définie dans la classe, qui agit sur un objet | `pikachu.attaquer(salameche)` |
|
|
| **Constructeur** | la méthode `__init__`, appelée à la création | remplit `nom`, `type_pokemon`, `pv` |
|
|
| **`self`** | l'objet sur lequel la méthode travaille | `pikachu` quand on écrit `pikachu.attaquer(...)` |
|
|
|
|
---
|
|
|
|
## 5. Les erreurs classiques
|
|
|
|
**Oublier `self` dans la définition d'une méthode.**
|
|
|
|
```python
|
|
def __init__(nom): # faux
|
|
def __init__(self, nom): # juste
|
|
```
|
|
|
|
Python répond : `TypeError: Pokemon.__init__() takes 1 positional argument but 2 were given`. Il a bien transmis deux choses, l'objet et le nom, mais la méthode n'avait prévu de place que pour une.
|
|
|
|
**Écrire `nom = nom` au lieu de `self.nom = nom`.**
|
|
|
|
Sans `self.`, la valeur est rangée dans une variable qui disparaît à la fin de `__init__`, pas dans l'objet. Plus tard, `pikachu.nom` provoque : `AttributeError: 'Pokemon' object has no attribute 'nom'`.
|
|
|
|
**Appeler une méthode sur la classe au lieu de l'objet.**
|
|
|
|
```python
|
|
Pokemon.subir_degats(10) # faux : quel Pokémon subit les dégâts ?
|
|
pikachu.subir_degats(10) # juste
|
|
```
|
|
|
|
C'est exactement la confusion entre classe et objet : la classe est un plan, on ne blesse pas un plan.
|
|
|
|
**Utiliser `print` au lieu de `return` dans `__repr__`.**
|
|
|
|
`__repr__` doit **renvoyer** un texte, pas l'afficher. Avec un `print`, Python se plaint : `TypeError: __str__ returned non-string (type NoneType)`.
|
|
|
|
---
|
|
|
|
## 6. Un peu d'histoire
|
|
|
|
!!! info "1967 : des classes pour simuler des navires"
|
|
|
|
À Oslo, deux chercheurs norvégiens, **Ole-Johan Dahl** et **Kristen Nygaard**, veulent simuler des systèmes réels : des files d'attente, du trafic, des navires dans un port. Ils créent le langage **Simula 67**, qui invente les **classes** et les **objets** : chaque navire du port devient un objet construit sur le modèle « navire ». C'est exactement l'idée de cette fiche, un plan et des exemplaires. Ils ont reçu en 2001 le **prix Turing**, l'équivalent du prix Nobel de l'informatique.
|
|
|
|
!!! info "Alan Kay et l'expression « orienté objet »"
|
|
|
|
Dans les années 1970, au centre de recherche de Xerox à Palo Alto, **Alan Kay** et son équipe créent le langage **Smalltalk**, où absolument tout est un objet. Alan Kay raconte que c'est lui qui a inventé l'expression « programmation orientée objet ». Des années plus tard, devant une salle d'informaticiens, il ajoutera avec malice : « J'ai inventé l'expression orienté objet, et je peux vous dire que je ne pensais pas à C++. »
|
|
|
|
!!! info "Et Python dans tout ça ?"
|
|
|
|
Python pousse cette idée aussi loin que Smalltalk : en Python, **tout est objet**. Les nombres, les textes, les listes, et même les fonctions et les classes elles-mêmes. C'est pour cela que `type(3)` répond `<class 'int'>` : depuis la Première, vous manipulez des objets.
|
|
|
|
---
|
|
|
|
## 7. Pour revoir en vidéo
|
|
|
|
[Programmation orientée objet - PYTHON - TLE - Spé NSI](https://www.youtube.com/watch?v=yncatRjlEa0&t=50s), par David Latouche (2020).
|
|
|
|
Regardez de **0:50 à 9:51** : les concepts clés, la déclaration d'une classe et le constructeur, l'instanciation d'un objet, les attributs et les méthodes. La suite (encapsulation, héritage, polymorphisme) dépasse le programme de Terminale.
|
|
|
|
---
|
|
|
|
## 8. À vous
|
|
|
|
!!! question "Exercice 1 : Classe ou objet ?"
|
|
|
|
Dans le code ci-dessous, indiquez pour chaque nom s'il désigne une **classe**, un **objet**, un **attribut** ou une **méthode** : `Dresseur`, `sacha`, `equipe`, `ajouter`, `pikachu`.
|
|
|
|
```python
|
|
class Dresseur:
|
|
def __init__(self, nom):
|
|
self.nom = nom
|
|
self.equipe = []
|
|
|
|
def ajouter(self, pokemon):
|
|
self.equipe.append(pokemon)
|
|
|
|
sacha = Dresseur("Sacha")
|
|
sacha.ajouter(pikachu)
|
|
```
|
|
|
|
??? success "Correction de l'exercice 1"
|
|
|
|
- `Dresseur` : une **classe** (mot-clé `class`, majuscule).
|
|
- `sacha` : un **objet**, créé par `Dresseur("Sacha")`.
|
|
- `equipe` : un **attribut** (`self.equipe`), la liste des Pokémon du dresseur.
|
|
- `ajouter` : une **méthode**, définie dans la classe et appelée sur l'objet `sacha`.
|
|
- `pikachu` : un **objet**, de la classe `Pokemon`. Ce n'est pas parce qu'il est rangé dans l'équipe de Sacha qu'il devient un `Dresseur`.
|
|
|
|
!!! question "Exercice 2 : Combien d'objets ?"
|
|
|
|
Avec la classe `Pokemon` de la partie 2 :
|
|
|
|
```python
|
|
a = Pokemon("Évoli", "Normal", 55)
|
|
b = Pokemon("Évoli", "Normal", 55)
|
|
c = a
|
|
b.subir_degats(10)
|
|
c.subir_degats(5)
|
|
```
|
|
|
|
1. Combien d'objets `Pokemon` ont été créés ?
|
|
2. Que valent `a.pv`, `b.pv` et `c.pv` ?
|
|
|
|
??? success "Correction de l'exercice 2"
|
|
|
|
1. **Deux objets** seulement. Seuls `Pokemon(...)` avec des parenthèses fabriquent un objet, et il y en a deux. `c = a` ne crée rien : c'est un **deuxième nom** pour le même objet que `a`.
|
|
2. `a.pv` vaut **50**, `b.pv` vaut **45** et `c.pv` vaut **50**. `b` a subi 10 dégâts. `c` et `a` étant le même Pokémon, les 5 dégâts infligés « à `c` » sont subis par `a`.
|
|
|
|
!!! question "Exercice 3 : Écrire une classe"
|
|
|
|
Écrivez une classe `Badge` dont le constructeur reçoit un `nom` et une `ville`, et range ces deux valeurs dans l'objet. Créez ensuite le badge « Roche » de la ville d'Argenta, puis affichez sa ville.
|
|
|
|
??? success "Correction de l'exercice 3"
|
|
|
|
```python
|
|
class Badge:
|
|
def __init__(self, nom, ville):
|
|
self.nom = nom
|
|
self.ville = ville
|
|
|
|
badge_roche = Badge("Roche", "Argenta")
|
|
print(badge_roche.ville)
|
|
```
|
|
|
|
Points à vérifier : `self` en premier paramètre de `__init__`, `self.nom` et `self.ville` (et pas `nom = nom`), et l'objet créé avec des parenthèses avant d'accéder à son attribut.
|
|
|
|
---
|
|
|
|
Auteur : Florian Mathieu
|
|
|
|
Licence CC BY-SA
|
|
|
|
<a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/"><img alt="Licence Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-sa/4.0/88x31.png" /></a> <br />Ce cours est mis à disposition selon les termes de la <a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/">Licence Creative Commons Attribution - Partage dans les Mêmes Conditions 4.0 International</a>.
|