Browse Source

Relecture épisode 5

master
Dimitri Merejkowsky 4 years ago
parent
commit
34c108abe9
1 changed files with 63 additions and 64 deletions
  1. +63
    -64
      saison-2/sessions/python-S02-E05.md

+ 63
- 64
saison-2/sessions/python-S02-E05.md View File

@@ -73,7 +73,7 @@ Ce qu'on a vu jusqu’ici:

* Des types simples (entiers, booléens, ...)
* Des structures de données (listes, dictionnaires, ...)
* Des fonctions qui manipulent ces types ou ces types
* Des fonctions qui manipulent ces données ou ces types
* Des fonctions qui s’appellent les unes les autres

On appelle cet ensemble de concepts, cette façon d'écrire du code, un *paradigme* -
@@ -109,9 +109,9 @@ L'important c'est que les deux aillent ensemble!
pour le moment ...*


# Les classes
# Classes et instances

On va parler *d'une* façon de faire de l'orienté objet: avec des classes.
On va parler *d'une* façon de faire de l'orienté objet: avec des classes et des instances.

Mais notez bien qu'on peut faire de l'orienté objet *sans* classes!

@@ -122,42 +122,42 @@ Pour construire un objet en Python, on a besoin d'un *plan de construction*.
On appelle ce plan une *classe* et on la définit ainsi:

```python
class MonObjet:
class MaClasse:
# du code ici
```

Comme les fonctions, les classes contiennent un *corps*, qui est le bloc *identé* en dessous
du mot-clé `class`, de nom de la classe et du `:` en fin de ligne

# Créons des objets
# Créons des instances

On peut faire un plan de construction vide avec le mot clé pass:

```python
class MonObjet:
class MaClasse:
pass
```

Dans ce cas, on crée un objet en mettant le nom de la classe suivi d'une paire de parenthèses -
Dans ce cas, on crée une instance en écrivant le nom de la classe suivi d'une paire de parenthèses -
un peu comme pour appeler une fonction:

```python
>>> objet_1 = MonObjet()
>>> mon_instance = MaClasse()
```

Ici, `objet_1` est une *instance* de la classe `MonObjet`.
Ici, `mon_instance` est une *instance* de la classe `MaClasse`.

# Attributs

Les attributs sont des éléments **nommés** à *l'intérieur* d'un objet.
Les attributs sont des éléments **nommés** à *l'intérieur* d'une instance.

On peut y accéder avec la syntaxe `<objet>.<attribut>`:
On peut y accéder avec la syntaxe `<instance>.<attribut>`:

```python
y = a.x
```

Ici, `y` est l'attribut `x` de l'objet `a`.
Ici, `y` est une variable qui a la valeur de l'attribut `x` de l'instance `a`.

# Attributs - 2

@@ -168,7 +168,7 @@ func = a.x
func(10)
```

Ici, on crée une variable `func` qui prend la valeur de l'attribut `x` dans l'objet `a`, puis
Ici, on crée une variable `func` qui prend la valeur de l'attribut `x` dans l'instance `a`, puis
on l'appelle avec l'argument `10` à la ligne suivante.

Le code suivant fait exactement la même chose, mais avec une ligne de moins:
@@ -194,10 +194,10 @@ On reviendra sur les modules dans un prochain chapitre.

# Attributs - 4

On peut *créer* des attributs dans *n'importe quel objet*, en utilisant l'*assignation*:
On peut *créer* des attributs dans *n'importe quel instance*, en utilisant l'*assignation*:

```python
>>> mon_instance = MonObjet()
>>> mon_instance = MaClasse()

# Création de l'attribut `x` dans `mon_instance`
>>> mon_instance.x = 42
@@ -215,29 +215,28 @@ On utilise `def`, comme pour les fonctions, mais les méthodes *doivent* avoir a
moins un argument appelé `self`, et être à l'intérieur du bloc de la classe:

```python
class MonObjet:
class MaClasse:
def ma_méthode(self):
return 42
```

# Méthodes - appel

Une méthode ne peut être appelée que depuis une *instance* de
l'objet:
Une méthode ne peut être appelée que depuis une *instance*:

```python
class MonObjet:
class MaClasse:
def ma_méthode(self):
return 42
>>> ma_méthode()
Erreur

>>> mon_instance = MonObjet()
>>> mon_instance = MaClasse()
>>> mon_instance.ma_méthode()
42
```

Notez qu'on ne passe *pas* d'argument quand on appelle `ma_méthode` depuis l'instance de l'objet.
Notez qu'on ne passe *pas* d'argument quand on appelle `ma_méthode` depuis l'instance de la classe.


# Méthodes et attributs - 1
@@ -247,13 +246,13 @@ Notez qu'on ne passe *pas* d'argument quand on appelle `ma_méthode` depuis l'in
On peut le voir en utilisant des attributs:

```python
class MonObjet:
class MaClasse:
def affiche_attribut_x(self):
# Accès à l'attribut `x` dans `self`
print(self.x)


>>> mon_instance = MonObjet()
>>> mon_instance = MaClasse()
>>> mon_instance.x = 42
>>> mon_instance.affiche_attribut_x()
42
@@ -264,13 +263,13 @@ class MonObjet:
On peut aussi *créer* des attributs dans une méthode:

```python
class MonObjet:
class MaClasse:
def crée_attribut_x(self):
self.x = 42
def affiche_attribut_x(self):
print(self.x)

>>> mon_instance = MonObjet()
>>> mon_instance = MaClasse()
>>> mon_instance.affiche_attribut_x()
# Erreur: `mon_instance` n'a pas d'attribut `x`

@@ -288,14 +287,14 @@ Par exemple, pour créer un attribut avec une certaine valeur:


```python
class MonObjet
class MaClasse
def crée_attribut_x(self, valeur_de_x):
self.x = valeur_de_x

def affiche_attribut_x(self);
print(self.x)

>>> mon_instance = MonObjet()
>>> mon_instance = MaClasse()
>>> mon_instance.crée_attribut_x(42)
>>> mon_instance.affiche_attribut_x()
42
@@ -303,11 +302,11 @@ class MonObjet

# Méthodes appelant d'autres méthodes - 1

Comme les méthodes sont *aussi* des attributs, les méthodes d'un objet peuvent s'appeler
Comme les méthodes sont *aussi* des attributs, les méthodes d'une classe peuvent s'appeler
les unes les autres:

```python
class MonObjet:
class MaClasse:
def méthode_1(self):
print("démarrage de la méthode 1")
print("la méthode 1 affiche bonjour")
@@ -324,7 +323,7 @@ class MonObjet:
# Méthodes appelant d'autres méthodes - 2

```python
>>> mon_instance = MonObjet()
>>> mon_instance = MaClasse()
>>> mon_instance.méthode_2()
```

@@ -340,7 +339,7 @@ fin de la méthode 2
# Une méthode spéciale

Si vous définissez une méthode `__init__`, celle-ci est appelée *automatiquement*
quand l'objet est construit.
quand l'instance est construite.

On dit que c'est une méthode "magique" parce qu'elle fait quelque chose _sans_ qu'on
l'appelle explicitement.
@@ -351,12 +350,12 @@ On utilise souvent `__init__` pour créer des attributs


```python
class MonObjet:
class MaClasse:
def __init__(self):
self.x = 1
self.y = 2

>>> mon_instance = MonObjet()
>>> mon_instance = MaClasse()

# __init__ est appelée automatiquement!
>>> mon_instance.x
@@ -370,7 +369,7 @@ class MonObjet:
On prend souvent les *valeurs* des attributs à créer en arguments de la méthode `__init__ `.

```python
class MonObjet:
class MaClasse:
def __init__(self, x, y):
self.x = x
self.y = y
@@ -380,21 +379,26 @@ Dans ce cas, les arguments de la méthode `__init__` apparaissent à l'intérieu
nom de la classe:

```
>>> mon_instance = MonObjet(3, 4)
>>> mon_instance = MaClasse(3, 4)
>>> mon_instance.x
3
>>> mon_instance.y
4
```

*Pour cette raison, `__init__` est souvent appelé le _constructeur_ de la classe.*
D'où le nom: `__init__` sert a initialiser les attributs de la classe.

# Constructeurs

Note: on appelle parfois la méthode `__init__` un constructeur, un terme qui est employé pour d'autres langages qui utilisent également des classes, comme Java ou C++.

Mais c'est un abus de langage: `__init__` en Python ne construit rien!

# Récapitulatif

* Classe: plan de construction
* Objet: ce qu'on crée avec le plan
* Attribut: variable dans un objet
* Instance: objet issue d'une classe
* Instance: ce qu'on construit avec une classe
* Attribut: variable dans une instance
* Méthode: fonction dans une classe (qui prend `self` en premier argument)
* `__init__`: méthode magique appelée automatiquement pendant l'instanciation

@@ -421,7 +425,7 @@ Ou encore *d'interface* et de *d'implémentation*.
# Exemple

```python
class MonObject:
class MaClasse:
def __init__(self):
# Notez le tiret bas en début
# du nom de l'attribut
@@ -430,7 +434,7 @@ class MonObject:
def ma_méthode_publique(self):
return self._mon_attribut_privé

>>> mon_objet = MonObject()
>>> mon_objet = MaClasse()
>>> mon_objet.ma_méthode_publique()
```

@@ -439,12 +443,12 @@ class MonObject:
Notez que rien ne vous empêche d'écrire:

```python
>>> mon_objet= MonObjet()
>>> mon_objet= MaClasse()
>>> mon_objet._mon_attribut_privé = "une-autre-valeur"
```

mais alors vous n'êtes plus dans le cas d'usage prévu
par l'auteur de la classe MonObjet.
par l'auteur de la classe MaClasse.

# Exemple d'usage - 1

@@ -457,7 +461,7 @@ fois :)
# Exemple d'usage - 2

```python
class MonObject:
class MaClasse:
def __init__(self):
self._mon_attribut_privé = None

@@ -467,7 +471,7 @@ class MonObject:
else:
return self._mon_attribut_privé

>>> mon_objet = MonObject()
>>> mon_objet = MaClasse()
>>> mon_objet.ma_méthode_publique()
# gros_calcul() est appelée
>>> mon_objet.ma_méthode_publique()
@@ -499,7 +503,7 @@ mais on n'en fait rien
#

```python
def différencernce(x, y):
def différence(x, y):
return x - y

z = différence(y=4, x=5)
@@ -518,11 +522,11 @@ Réponse: 1 Quand on nomme les arguments, on les mets dans l'ordre qu'on veut.
#

```python
class MonObject:
class MaClasse:
def ma_méthode():
print("Bonjour")

mon_objet = MonObject()
mon_objet = MaClasse()
mon_objet.ma_méthode()
```

@@ -536,12 +540,12 @@ Réponse 1: les méthodes prennent `self` en premier argument
#

```python
class MonObject:
def ma_méthode():
class MaClasse:
def ma_méthode(self):
print("mon attribut est", self.mon_attribut)

mon_attribut = 42
mon_objet = MonObject()
mon_objet = MaClasse()
mon_objet.ma_méthode()
```

@@ -555,11 +559,11 @@ Réponse 2: Les attributs doivent exister quand ils sont utilisés comme valeur
#

```python
class MonObject:
class MaClasse:
def ma_méthode(self):
print(self.mon_attribut)

mon_objet = MonObject()
mon_objet = MaClasse()
mon_objet.mon_attribut = 42
mon_objet.ma_méthode()
```
@@ -574,12 +578,12 @@ Réponse 2: On peut créer des attributs avec des assignations
#

```python
class MonObject:
class MaClasse:
def ma_méthode(self):
self.mon_attribut = 42
print("mon attribut est", self.mon_attribut)

mon_objet = MonObject()
mon_objet = MaClasse()
mon_objet.ma_méthode()
```

@@ -594,11 +598,11 @@ des méthodes grâce à `self`.
#

```python
class MonObject:
class MaClasse:
def __init__(self, valeur):
self.mon_attribut = valeur

mon_objet = MonObject()
mon_objet = MaClasse()
valeur = mon_objet.mon_attribut
print(valeur)
```
@@ -608,7 +612,8 @@ print(valeur)

\pause

Réponse 2: `__init__` est une méthode magique appelée automatiquement.
Réponse 1: `__init__` est une méthode magique appelée automatiquement,
donc il *faut* passer un argument quand on instancie la classe: `mon_objet = MaClasse(42)`

#

@@ -629,9 +634,3 @@ Vous êtes développeur dans une usine de fabrication de robots.

* Un squelette `robot.py`, à récupérer sur `git.e2li.org`
* Contient déjà le `main()` de test - à vous d'implémenter la classe!


# Pour aller plus loin

* Le robot n'a pas le droit d'avoir deux fois le même nom. À vous de coder cela!