|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636 |
- % Programmation avec Python (chapitre 5)
- % Dimitri Merejkowsky
-
-
- #
-
- \center \huge Rappels sur les fonctions
-
- # Exemple 1
-
- ```python
- # Définition d'une fonction sans arguments
- def ma_fonction():
- print("ma_fonction commence ...")
- print("bonjour")
- print("ma_fonction finit.")
-
- # Appel de la fonction `ma_fonction`:
- >>> ma_fonction()
- ma_fonction commence ...
- bonjour
- ma_fonction finit.
- ```
-
- # Exemple 2
-
- ```python
- # Définition d'une fonction avec un argument, x:
- def ma_fonction(x):
- print("x vaut", x)
-
- # Appel de la fonction `ma_fonction`:
- >>> ma_fonction(42)
- x vaut 42
- ```
-
- # Aparté - le mot-clé `pass`
-
- En Python, à cause de l'organisation en blocs indentés, on ne
- peut pas vraiment avoir de blocs vides. Mais parfois, on
- a besoin d'un bloc qui ne fasse rien.
-
- Dans ce cas, on peut utiliser le mot-clé `pass`, par exemple
- après un if:
-
- ```python
- une_condition = False
- if une_condition:
- pass
- else:
- print("une_condition n'est pas vraie")
- ```
-
- # Le mot-clé `pass` - 2
-
- On peut aussi - et c'est l'usage le plus courant - utiliser `pass` pour
- définir une fonction qui ne fait rien:
-
- ```python
- def ne_fait_rien():
- pass
- ```
-
- ```python
- >>> ne_fait_rien()
- <rien>
- ```
-
-
- # Changement de paradigme
-
- 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 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* -
- et c'est un paradigme *procédural*.
-
- On va passer à un autre paradigme: l'*orienté objet*.
-
- # Orienté objet - une première définition
-
- Un "objet" informatique *représente* un véritable "objet" physique
- dans le vrai monde véritable.
-
- Ce n'est pas une très bonne définition:
-
- 1. Ce n'est pas nécessaire
- 2. Ce n'est même pas forcément souhaitable!
-
- Je le mentionne juste parce que c'est une idée reçue très répandue.
-
- # Orienté objet - 2ème définition
-
- Une meilleure définition, c'est de dire que la programmation
- orientée objet permet de mettre au même endroit:
-
- * des données
- * des fonctions qui opèrent sur ces données
-
- L'important c'est que les deux aillent ensemble!
-
- \vfill
-
- *Note: ce n'est pas **la** meilleure définition de l'orienté objet, mais on s'en contentera
- pour le moment ...*
-
-
- # Classes et instances
-
- 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!
-
- # Le plan de construction
-
- 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 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 instances
-
- On peut faire un plan de construction vide avec le mot clé pass:
-
- ```python
- class MaClasse:
- pass
- ```
-
- 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
- >>> mon_instance = MaClasse()
- ```
-
- Ici, `mon_instance` est une *instance* de la classe `MaClasse`.
-
- # Attributs
-
- Les attributs sont des éléments **nommés** à *l'intérieur* d'une instance.
-
- On peut y accéder avec la syntaxe `<instance>.<attribut>`:
-
- ```python
- y = a.x
- ```
-
- Ici, `y` est une variable qui a la valeur de l'attribut `x` de l'instance `a`.
-
- # Attributs - 2
-
- Les attributs peuvent être des fonctions:
-
- ```python
- func = a.x
- func(10)
- ```
-
- 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:
-
- ```python
- a.x(10)
- ```
-
- # Attributs - 3
-
- On a déjà vu des attributs, quand on a utilisé des `modules`
-
- ```python
- import random
-
- nombre_au_hasard = random.randint(0, 10)
- ```
-
- Ici, `random` est un module, et `randint` est un *attribut* du module `random`. Il se trouve
- que cet attribut est une fonction qu'on peut appeler avec deux arguments.
-
- On reviendra sur les modules dans un prochain chapitre.
-
- # Attributs - 4
-
- On peut *créer* des attributs dans *n'importe quel instance*, en utilisant l'*assignation*:
-
- ```python
- >>> mon_instance = MaClasse()
-
- # Création de l'attribut `x` dans `mon_instance`
- >>> mon_instance.x = 42
-
- # Accès à l'attribut `x` dans `mon_instance`
- >>> mon_instance.mon_attribut
- 42
- ```
-
- # Méthodes - définition
-
- On peut aussi mettre des *méthodes* dans des classes.
-
- On utilise `def`, comme pour les fonctions, mais les méthodes *doivent* avoir au
- moins un argument appelé `self`, et être à l'intérieur du bloc de la classe:
-
- ```python
- class MaClasse:
- def ma_méthode(self):
- return 42
- ```
-
- # Méthodes - appel
-
- Une méthode ne peut être appelée que depuis une *instance*:
-
- ```python
- class MaClasse:
- def ma_méthode(self):
- return 42
- >>> ma_méthode()
- Erreur
-
- >>> 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 la classe.
-
-
- # Méthodes et attributs - 1
-
- `self` *prend la valeur de l'instance courante* quand la méthode est appelée.
-
- On peut le voir en utilisant des attributs:
-
- ```python
- class MaClasse:
- def affiche_attribut_x(self):
- # Accès à l'attribut `x` dans `self`
- print(self.x)
-
-
- >>> mon_instance = MaClasse()
- >>> mon_instance.x = 42
- >>> mon_instance.affiche_attribut_x()
- 42
- ```
-
- # Méthodes et attributs - 2
-
- On peut aussi *créer* des attributs dans une méthode:
-
- ```python
- class MaClasse:
- def crée_attribut_x(self):
- self.x = 42
- def affiche_attribut_x(self):
- print(self.x)
-
- >>> mon_instance = MaClasse()
- >>> mon_instance.affiche_attribut_x()
- # Erreur: `mon_instance` n'a pas d'attribut `x`
-
- >>> mon_instance.crée_attribut_x()
- >>> mon_instance.affiche_attribut_x()
- 42
- ```
-
- # Méthodes et attributs - 3
-
- Les méthodes peuvent aussi prendre plusieurs arguments, en plus de `self` - mais `self` doit
- toujours être le premier argument.
-
- Par exemple, pour créer un attribut avec une certaine valeur:
-
-
- ```python
- 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 = MaClasse()
- >>> mon_instance.crée_attribut_x(42)
- >>> mon_instance.affiche_attribut_x()
- 42
- ```
-
- # Méthodes appelant d'autres méthodes - 1
-
- Comme les méthodes sont *aussi* des attributs, les méthodes d'une classe peuvent s'appeler
- les unes les autres:
-
- ```python
- class MaClasse:
- def méthode_1(self):
- print("démarrage de la méthode 1")
- print("la méthode 1 affiche bonjour")
- print("bonjour")
- print("fin de la méthode 1")
-
-
- def méthode_2(self):
- print("la méthode 2 appelle la méthode 1")
- self.méthode_1()
- print("fin de la méthode 2")
- ```
-
- # Méthodes appelant d'autres méthodes - 2
-
- ```python
- >>> mon_instance = MaClasse()
- >>> mon_instance.méthode_2()
- ```
-
- ```text
- la méthode 2 appelle la méthode 1
- démarrage de la méthode 1
- la méthode 1 affiche bonjour
- bonjour
- fin de la méthode 1
- 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'instance est construite.
-
- On dit que c'est une méthode "magique" parce qu'elle fait quelque chose _sans_ qu'on
- l'appelle explicitement.
-
- # \_\_init\_\_
-
- On utilise souvent `__init__` pour créer des attributs
-
-
- ```python
- class MaClasse:
- def __init__(self):
- self.x = 1
- self.y = 2
-
- >>> mon_instance = MaClasse()
-
- # __init__ est appelée automatiquement!
- >>> mon_instance.x
- 1
- >>> mon_instance.y
- 2
- ```
-
- # \_\_init\_\_ - 2
-
- On prend souvent les *valeurs* des attributs à créer en arguments de la méthode `__init__ `.
-
- ```python
- class MaClasse:
- def __init__(self, x, y):
- self.x = x
- self.y = y
- ```
-
- Dans ce cas, les arguments de la méthode `__init__` apparaissent à l'intérieur des parenthèses après le
- nom de la classe:
-
- ```
- >>> mon_instance = MaClasse(3, 4)
- >>> mon_instance.x
- 3
- >>> mon_instance.y
- 4
- ```
-
- 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
- * 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
-
-
- # Classes et programmation orienté objet
-
- Ainsi, on peut ranger au même endroit des données et des fonctions opérant sur ces données.
-
- Les données sont les attributs, et les fonctions opérant sur ces attributs sont les méthodes.
-
- On peut ainsi séparer les *responsabilités* à l'intérieur d'un code en les répartissant
- entres plusieurs classes.
-
- # Encapsulation
-
- Définition: cacher à l'utilisateur de la classe certains détails du
- fonctionnement de celle-ci.
-
- On parle souvent d'opposition entre code *public*, utilisable à l'extérieur,
- de la classe, et code *privé*, utilisé à l'intérieur de la classe.
-
- Ou encore *d'interface* et de *d'implémentation*.
-
- # Exemple
-
- ```python
- class MaClasse:
- def __init__(self):
- # Notez le tiret bas en début
- # du nom de l'attribut
- self._mon_attribut_privé = ...
-
- def ma_méthode_publique(self):
- return self._mon_attribut_privé
-
- >>> mon_objet = MaClasse()
- >>> mon_objet.ma_méthode_publique()
- ```
-
- # Conventions
-
- Notez que rien ne vous empêche d'écrire:
-
- ```python
- >>> 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 MaClasse.
-
- # Exemple d'usage - 1
-
- Si `_mon_attribut_privé` demande de longs calculs, on peut envisager de stocker le résultat
- de façon à ce que le deuxième appel à `ma_méthode_publique()` soit plus rapide.
-
- De l'extérieur, l'appel à `ma_méthode_publique()` sera "magiquement" plus rapide la deuxième
- fois :)
-
- # Exemple d'usage - 2
-
- ```python
- class MaClasse:
- def __init__(self):
- self._mon_attribut_privé = None
-
- def ma_méthode_publique(self):
- if self._mon_attribut_privé is None:
- self._mon_attribut_privé = gros_calcul()
- else:
- return self._mon_attribut_privé
-
- >>> mon_objet = MaClasse()
- >>> mon_objet.ma_méthode_publique()
- # gros_calcul() est appelée
- >>> mon_objet.ma_méthode_publique()
- # retourne une valeur immédiatement!
- ```
-
- #
-
- \center \huge QCM
-
- #
-
- ```python
- def dire_bonjour():
- return "Bonjour"
-
- dire_bonjour()
- ```
-
- 1. Erreur
- 2. Affiche "Bonjour"
- 3. N'affiche rien
-
- \pause
-
- Réponse: 3: la fonction retourne quelque chose,
- mais on n'en fait rien
-
- #
-
- ```python
- def différence(x, y):
- return x - y
-
- z = différence(y=4, x=5)
- ```
-
- Que vaut `z`?
-
- 1. `1`
- 2. `-1`
- 3. Erreur
-
- \pause
-
- Réponse: 1 Quand on nomme les arguments, on les mets dans l'ordre qu'on veut.
-
- #
-
- ```python
- class MaClasse:
- def ma_méthode():
- print("Bonjour")
-
- mon_objet = MaClasse()
- mon_objet.ma_méthode()
- ```
-
- 1. Erreur
- 2. Affiche 'Bonjour'
-
- \pause
-
- Réponse 1: les méthodes prennent `self` en premier argument
-
- #
-
- ```python
- class MaClasse:
- def ma_méthode(self):
- print("mon attribut est", self.mon_attribut)
-
- mon_attribut = 42
- mon_objet = MaClasse()
- mon_objet.ma_méthode()
- ```
-
- 1. Affiche "mon attribut est 42'
- 2. Erreur
-
- \pause
-
- Réponse 2: Les attributs doivent exister quand ils sont utilisés comme valeur
-
- #
-
- ```python
- class MaClasse:
- def ma_méthode(self):
- print(self.mon_attribut)
-
- mon_objet = MaClasse()
- mon_objet.mon_attribut = 42
- mon_objet.ma_méthode()
- ```
-
- 1. Erreur
- 2. Affiche '42'
-
- \pause
-
- Réponse 2: On peut créer des attributs avec des assignations
-
- #
-
- ```python
- class MaClasse:
- def ma_méthode(self):
- self.mon_attribut = 42
- print("mon attribut est", self.mon_attribut)
-
- mon_objet = MaClasse()
- mon_objet.ma_méthode()
- ```
-
- 1. Affiche "mon attribut est 42'
- 2. Erreur
-
- \pause
-
- Réponse 1: On peut créer des attributs dans
- des méthodes grâce à `self`.
-
- #
-
- ```python
- class MaClasse:
- def __init__(self, valeur):
- self.mon_attribut = valeur
-
- mon_objet = MaClasse()
- valeur = mon_objet.mon_attribut
- print(valeur)
- ```
-
- 1. Erreur
- 2. Affiche '42'
-
- \pause
-
- 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)`
-
- #
-
- \center \huge Atelier
-
- # Consignes
-
- Vous êtes développeur dans une usine de fabrication de robots.
-
- * Quand les robots sortent de la chaîne de montage, ils n'ont pas encore de nom
- * La première fois qu'on démarre un robot, un nom est généré au hasard, avec
- le format suivant: deux lettres majuscules et trois chiffres. Par exemple:
- `RX837` ou `BC811`
- * De temps en temps, les robots sont remis à aux paramètres d'usine, le nom
- est effacé et doit être regénéré
-
- # Pour vous aider
-
- * Un squelette `robot.py`, à récupérer sur `git.e2li.org`
- * Contient déjà le `main()` de test - à vous d'implémenter la classe!
|