You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

python-S02-E05.md 13 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636
  1. % Programmation avec Python (chapitre 5)
  2. % Dimitri Merejkowsky
  3. #
  4. \center \huge Rappels sur les fonctions
  5. # Exemple 1
  6. ```python
  7. # Définition d'une fonction sans arguments
  8. def ma_fonction():
  9. print("ma_fonction commence ...")
  10. print("bonjour")
  11. print("ma_fonction finit.")
  12. # Appel de la fonction `ma_fonction`:
  13. >>> ma_fonction()
  14. ma_fonction commence ...
  15. bonjour
  16. ma_fonction finit.
  17. ```
  18. # Exemple 2
  19. ```python
  20. # Définition d'une fonction avec un argument, x:
  21. def ma_fonction(x):
  22. print("x vaut", x)
  23. # Appel de la fonction `ma_fonction`:
  24. >>> ma_fonction(42)
  25. x vaut 42
  26. ```
  27. # Aparté - le mot-clé `pass`
  28. En Python, à cause de l'organisation en blocs indentés, on ne
  29. peut pas vraiment avoir de blocs vides. Mais parfois, on
  30. a besoin d'un bloc qui ne fasse rien.
  31. Dans ce cas, on peut utiliser le mot-clé `pass`, par exemple
  32. après un if:
  33. ```python
  34. une_condition = False
  35. if une_condition:
  36. pass
  37. else:
  38. print("une_condition n'est pas vraie")
  39. ```
  40. # Le mot-clé `pass` - 2
  41. On peut aussi - et c'est l'usage le plus courant - utiliser `pass` pour
  42. définir une fonction qui ne fait rien:
  43. ```python
  44. def ne_fait_rien():
  45. pass
  46. ```
  47. ```python
  48. >>> ne_fait_rien()
  49. <rien>
  50. ```
  51. # Changement de paradigme
  52. Ce qu'on a vu jusqu’ici:
  53. * Des types simples (entiers, booléens, ...)
  54. * Des structures de données (listes, dictionnaires, ...)
  55. * Des fonctions qui manipulent ces données ou ces types
  56. * Des fonctions qui s’appellent les unes les autres
  57. On appelle cet ensemble de concepts, cette façon d'écrire du code, un *paradigme* -
  58. et c'est un paradigme *procédural*.
  59. On va passer à un autre paradigme: l'*orienté objet*.
  60. # Orienté objet - une première définition
  61. Un "objet" informatique *représente* un véritable "objet" physique
  62. dans le vrai monde véritable.
  63. Ce n'est pas une très bonne définition:
  64. 1. Ce n'est pas nécessaire
  65. 2. Ce n'est même pas forcément souhaitable!
  66. Je le mentionne juste parce que c'est une idée reçue très répandue.
  67. # Orienté objet - 2ème définition
  68. Une meilleure définition, c'est de dire que la programmation
  69. orientée objet permet de mettre au même endroit:
  70. * des données
  71. * des fonctions qui opèrent sur ces données
  72. L'important c'est que les deux aillent ensemble!
  73. \vfill
  74. *Note: ce n'est pas **la** meilleure définition de l'orienté objet, mais on s'en contentera
  75. pour le moment ...*
  76. # Classes et instances
  77. On va parler *d'une* façon de faire de l'orienté objet: avec des classes et des instances.
  78. Mais notez bien qu'on peut faire de l'orienté objet *sans* classes!
  79. # Le plan de construction
  80. Pour construire un objet en Python, on a besoin d'un *plan de construction*.
  81. On appelle ce plan une *classe* et on la définit ainsi:
  82. ```python
  83. class MaClasse:
  84. # du code ici
  85. ```
  86. Comme les fonctions, les classes contiennent un *corps*, qui est le bloc *identé* en dessous
  87. du mot-clé `class`, de nom de la classe et du `:` en fin de ligne
  88. # Créons des instances
  89. On peut faire un plan de construction vide avec le mot clé pass:
  90. ```python
  91. class MaClasse:
  92. pass
  93. ```
  94. Dans ce cas, on crée une instance en écrivant le nom de la classe suivi d'une paire de parenthèses -
  95. un peu comme pour appeler une fonction:
  96. ```python
  97. >>> mon_instance = MaClasse()
  98. ```
  99. Ici, `mon_instance` est une *instance* de la classe `MaClasse`.
  100. # Attributs
  101. Les attributs sont des éléments **nommés** à *l'intérieur* d'une instance.
  102. On peut y accéder avec la syntaxe `<instance>.<attribut>`:
  103. ```python
  104. y = a.x
  105. ```
  106. Ici, `y` est une variable qui a la valeur de l'attribut `x` de l'instance `a`.
  107. # Attributs - 2
  108. Les attributs peuvent être des fonctions:
  109. ```python
  110. func = a.x
  111. func(10)
  112. ```
  113. Ici, on crée une variable `func` qui prend la valeur de l'attribut `x` dans l'instance `a`, puis
  114. on l'appelle avec l'argument `10` à la ligne suivante.
  115. Le code suivant fait exactement la même chose, mais avec une ligne de moins:
  116. ```python
  117. a.x(10)
  118. ```
  119. # Attributs - 3
  120. On a déjà vu des attributs, quand on a utilisé des `modules`
  121. ```python
  122. import random
  123. nombre_au_hasard = random.randint(0, 10)
  124. ```
  125. Ici, `random` est un module, et `randint` est un *attribut* du module `random`. Il se trouve
  126. que cet attribut est une fonction qu'on peut appeler avec deux arguments.
  127. On reviendra sur les modules dans un prochain chapitre.
  128. # Attributs - 4
  129. On peut *créer* des attributs dans *n'importe quel instance*, en utilisant l'*assignation*:
  130. ```python
  131. >>> mon_instance = MaClasse()
  132. # Création de l'attribut `x` dans `mon_instance`
  133. >>> mon_instance.x = 42
  134. # Accès à l'attribut `x` dans `mon_instance`
  135. >>> mon_instance.mon_attribut
  136. 42
  137. ```
  138. # Méthodes - définition
  139. On peut aussi mettre des *méthodes* dans des classes.
  140. On utilise `def`, comme pour les fonctions, mais les méthodes *doivent* avoir au
  141. moins un argument appelé `self`, et être à l'intérieur du bloc de la classe:
  142. ```python
  143. class MaClasse:
  144. def ma_méthode(self):
  145. return 42
  146. ```
  147. # Méthodes - appel
  148. Une méthode ne peut être appelée que depuis une *instance*:
  149. ```python
  150. class MaClasse:
  151. def ma_méthode(self):
  152. return 42
  153. >>> ma_méthode()
  154. Erreur
  155. >>> mon_instance = MaClasse()
  156. >>> mon_instance.ma_méthode()
  157. 42
  158. ```
  159. Notez qu'on ne passe *pas* d'argument quand on appelle `ma_méthode` depuis l'instance de la classe.
  160. # Méthodes et attributs - 1
  161. `self` *prend la valeur de l'instance courante* quand la méthode est appelée.
  162. On peut le voir en utilisant des attributs:
  163. ```python
  164. class MaClasse:
  165. def affiche_attribut_x(self):
  166. # Accès à l'attribut `x` dans `self`
  167. print(self.x)
  168. >>> mon_instance = MaClasse()
  169. >>> mon_instance.x = 42
  170. >>> mon_instance.affiche_attribut_x()
  171. 42
  172. ```
  173. # Méthodes et attributs - 2
  174. On peut aussi *créer* des attributs dans une méthode:
  175. ```python
  176. class MaClasse:
  177. def crée_attribut_x(self):
  178. self.x = 42
  179. def affiche_attribut_x(self):
  180. print(self.x)
  181. >>> mon_instance = MaClasse()
  182. >>> mon_instance.affiche_attribut_x()
  183. # Erreur: `mon_instance` n'a pas d'attribut `x`
  184. >>> mon_instance.crée_attribut_x()
  185. >>> mon_instance.affiche_attribut_x()
  186. 42
  187. ```
  188. # Méthodes et attributs - 3
  189. Les méthodes peuvent aussi prendre plusieurs arguments, en plus de `self` - mais `self` doit
  190. toujours être le premier argument.
  191. Par exemple, pour créer un attribut avec une certaine valeur:
  192. ```python
  193. class MaClasse
  194. def crée_attribut_x(self, valeur_de_x):
  195. self.x = valeur_de_x
  196. def affiche_attribut_x(self);
  197. print(self.x)
  198. >>> mon_instance = MaClasse()
  199. >>> mon_instance.crée_attribut_x(42)
  200. >>> mon_instance.affiche_attribut_x()
  201. 42
  202. ```
  203. # Méthodes appelant d'autres méthodes - 1
  204. Comme les méthodes sont *aussi* des attributs, les méthodes d'une classe peuvent s'appeler
  205. les unes les autres:
  206. ```python
  207. class MaClasse:
  208. def méthode_1(self):
  209. print("démarrage de la méthode 1")
  210. print("la méthode 1 affiche bonjour")
  211. print("bonjour")
  212. print("fin de la méthode 1")
  213. def méthode_2(self):
  214. print("la méthode 2 appelle la méthode 1")
  215. self.méthode_1()
  216. print("fin de la méthode 2")
  217. ```
  218. # Méthodes appelant d'autres méthodes - 2
  219. ```python
  220. >>> mon_instance = MaClasse()
  221. >>> mon_instance.méthode_2()
  222. ```
  223. ```text
  224. la méthode 2 appelle la méthode 1
  225. démarrage de la méthode 1
  226. la méthode 1 affiche bonjour
  227. bonjour
  228. fin de la méthode 1
  229. fin de la méthode 2
  230. ```
  231. # Une méthode spéciale
  232. Si vous définissez une méthode `__init__`, celle-ci est appelée *automatiquement*
  233. quand l'instance est construite.
  234. On dit que c'est une méthode "magique" parce qu'elle fait quelque chose _sans_ qu'on
  235. l'appelle explicitement.
  236. # \_\_init\_\_
  237. On utilise souvent `__init__` pour créer des attributs
  238. ```python
  239. class MaClasse:
  240. def __init__(self):
  241. self.x = 1
  242. self.y = 2
  243. >>> mon_instance = MaClasse()
  244. # __init__ est appelée automatiquement!
  245. >>> mon_instance.x
  246. 1
  247. >>> mon_instance.y
  248. 2
  249. ```
  250. # \_\_init\_\_ - 2
  251. On prend souvent les *valeurs* des attributs à créer en arguments de la méthode `__init__ `.
  252. ```python
  253. class MaClasse:
  254. def __init__(self, x, y):
  255. self.x = x
  256. self.y = y
  257. ```
  258. Dans ce cas, les arguments de la méthode `__init__` apparaissent à l'intérieur des parenthèses après le
  259. nom de la classe:
  260. ```
  261. >>> mon_instance = MaClasse(3, 4)
  262. >>> mon_instance.x
  263. 3
  264. >>> mon_instance.y
  265. 4
  266. ```
  267. D'où le nom: `__init__` sert a initialiser les attributs de la classe.
  268. # Constructeurs
  269. 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++.
  270. Mais c'est un abus de langage: `__init__` en Python ne construit rien!
  271. # Récapitulatif
  272. * Classe: plan de construction
  273. * Instance: ce qu'on construit avec une classe
  274. * Attribut: variable dans une instance
  275. * Méthode: fonction dans une classe (qui prend `self` en premier argument)
  276. * `__init__`: méthode magique appelée automatiquement pendant l'instanciation
  277. # Classes et programmation orienté objet
  278. Ainsi, on peut ranger au même endroit des données et des fonctions opérant sur ces données.
  279. Les données sont les attributs, et les fonctions opérant sur ces attributs sont les méthodes.
  280. On peut ainsi séparer les *responsabilités* à l'intérieur d'un code en les répartissant
  281. entres plusieurs classes.
  282. # Encapsulation
  283. Définition: cacher à l'utilisateur de la classe certains détails du
  284. fonctionnement de celle-ci.
  285. On parle souvent d'opposition entre code *public*, utilisable à l'extérieur,
  286. de la classe, et code *privé*, utilisé à l'intérieur de la classe.
  287. Ou encore *d'interface* et de *d'implémentation*.
  288. # Exemple
  289. ```python
  290. class MaClasse:
  291. def __init__(self):
  292. # Notez le tiret bas en début
  293. # du nom de l'attribut
  294. self._mon_attribut_privé = ...
  295. def ma_méthode_publique(self):
  296. return self._mon_attribut_privé
  297. >>> mon_objet = MaClasse()
  298. >>> mon_objet.ma_méthode_publique()
  299. ```
  300. # Conventions
  301. Notez que rien ne vous empêche d'écrire:
  302. ```python
  303. >>> mon_objet= MaClasse()
  304. >>> mon_objet._mon_attribut_privé = "une-autre-valeur"
  305. ```
  306. mais alors vous n'êtes plus dans le cas d'usage prévu
  307. par l'auteur de la classe MaClasse.
  308. # Exemple d'usage - 1
  309. Si `_mon_attribut_privé` demande de longs calculs, on peut envisager de stocker le résultat
  310. de façon à ce que le deuxième appel à `ma_méthode_publique()` soit plus rapide.
  311. De l'extérieur, l'appel à `ma_méthode_publique()` sera "magiquement" plus rapide la deuxième
  312. fois :)
  313. # Exemple d'usage - 2
  314. ```python
  315. class MaClasse:
  316. def __init__(self):
  317. self._mon_attribut_privé = None
  318. def ma_méthode_publique(self):
  319. if self._mon_attribut_privé is None:
  320. self._mon_attribut_privé = gros_calcul()
  321. else:
  322. return self._mon_attribut_privé
  323. >>> mon_objet = MaClasse()
  324. >>> mon_objet.ma_méthode_publique()
  325. # gros_calcul() est appelée
  326. >>> mon_objet.ma_méthode_publique()
  327. # retourne une valeur immédiatement!
  328. ```
  329. #
  330. \center \huge QCM
  331. #
  332. ```python
  333. def dire_bonjour():
  334. return "Bonjour"
  335. dire_bonjour()
  336. ```
  337. 1. Erreur
  338. 2. Affiche "Bonjour"
  339. 3. N'affiche rien
  340. \pause
  341. Réponse: 3: la fonction retourne quelque chose,
  342. mais on n'en fait rien
  343. #
  344. ```python
  345. def différence(x, y):
  346. return x - y
  347. z = différence(y=4, x=5)
  348. ```
  349. Que vaut `z`?
  350. 1. `1`
  351. 2. `-1`
  352. 3. Erreur
  353. \pause
  354. Réponse: 1 Quand on nomme les arguments, on les mets dans l'ordre qu'on veut.
  355. #
  356. ```python
  357. class MaClasse:
  358. def ma_méthode():
  359. print("Bonjour")
  360. mon_objet = MaClasse()
  361. mon_objet.ma_méthode()
  362. ```
  363. 1. Erreur
  364. 2. Affiche 'Bonjour'
  365. \pause
  366. Réponse 1: les méthodes prennent `self` en premier argument
  367. #
  368. ```python
  369. class MaClasse:
  370. def ma_méthode(self):
  371. print("mon attribut est", self.mon_attribut)
  372. mon_attribut = 42
  373. mon_objet = MaClasse()
  374. mon_objet.ma_méthode()
  375. ```
  376. 1. Affiche "mon attribut est 42'
  377. 2. Erreur
  378. \pause
  379. Réponse 2: Les attributs doivent exister quand ils sont utilisés comme valeur
  380. #
  381. ```python
  382. class MaClasse:
  383. def ma_méthode(self):
  384. print(self.mon_attribut)
  385. mon_objet = MaClasse()
  386. mon_objet.mon_attribut = 42
  387. mon_objet.ma_méthode()
  388. ```
  389. 1. Erreur
  390. 2. Affiche '42'
  391. \pause
  392. Réponse 2: On peut créer des attributs avec des assignations
  393. #
  394. ```python
  395. class MaClasse:
  396. def ma_méthode(self):
  397. self.mon_attribut = 42
  398. print("mon attribut est", self.mon_attribut)
  399. mon_objet = MaClasse()
  400. mon_objet.ma_méthode()
  401. ```
  402. 1. Affiche "mon attribut est 42'
  403. 2. Erreur
  404. \pause
  405. Réponse 1: On peut créer des attributs dans
  406. des méthodes grâce à `self`.
  407. #
  408. ```python
  409. class MaClasse:
  410. def __init__(self, valeur):
  411. self.mon_attribut = valeur
  412. mon_objet = MaClasse()
  413. valeur = mon_objet.mon_attribut
  414. print(valeur)
  415. ```
  416. 1. Erreur
  417. 2. Affiche '42'
  418. \pause
  419. Réponse 1: `__init__` est une méthode magique appelée automatiquement,
  420. donc il *faut* passer un argument quand on instancie la classe: `mon_objet = MaClasse(42)`
  421. #
  422. \center \huge Atelier
  423. # Consignes
  424. Vous êtes développeur dans une usine de fabrication de robots.
  425. * Quand les robots sortent de la chaîne de montage, ils n'ont pas encore de nom
  426. * La première fois qu'on démarre un robot, un nom est généré au hasard, avec
  427. le format suivant: deux lettres majuscules et trois chiffres. Par exemple:
  428. `RX837` ou `BC811`
  429. * De temps en temps, les robots sont remis à aux paramètres d'usine, le nom
  430. est effacé et doit être regénéré
  431. # Pour vous aider
  432. * Un squelette `robot.py`, à récupérer sur `git.e2li.org`
  433. * Contient déjà le `main()` de test - à vous d'implémenter la classe!