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-06.md 8.7 KiB

5 years ago
5 years ago
5 years ago
5 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. % Programmation avec Python (chapitre 6)
  2. % Dimitri Merejkowsky
  3. #
  4. \center \huge Orienté objet et classes
  5. # Paradigmes
  6. Une façon d'envisager le code.
  7. Pour l'instant on n'a vu que le paradigme *procédural* (ou (impératif).
  8. Il y en a plein d'autres! (*fonctionnel* notamment, dont on parlera un jour)
  9. # Détails du procédural
  10. * des types simples (entiers, booléens)
  11. * des structures de données (dictionnaires, listes ...)
  12. * des fonctions qui manipulent des types simples ou des structures
  13. * les fonctions sont appelées les unes après les autres
  14. Aujourd'hui on va parler de *l'orienté objet*.
  15. # Un petit détour
  16. Un nouveau built-in: `id()`
  17. L'adresse de l'objet pointé par la variable:
  18. ```
  19. >>> a = 42532
  20. >>> id(a)
  21. 94295009035968
  22. >>> b = a
  23. >>> id(b) # même objet
  24. 94295009035968
  25. >>> c = 42532 # objet différent, même valeur
  26. >>> id(c)
  27. ```
  28. # Orienté objet - 1ère définition
  29. Mettre au même endroit:
  30. * des données
  31. * des fonctions qui opèrent sur ces données
  32. L'important c'est que les deux aillent ensemble
  33. OOP en Anglais (ou juste OO)
  34. # Orienté objet - 2ème définition
  35. Des "cellules" qui s'envoient des "messages".
  36. Notamment, les cellules ne "voient" que leur état interne.
  37. On peut envoyer un message d'une cellule à une autre *sans* connaître
  38. beaucoup de détails à propos du destinataire du message
  39. # Les classes
  40. On va parler *d'une* façon de faire de l'orienté objet: avec des classes.
  41. Mais notez bien qu'on peut faire de l'orienté objet *sans* classes!
  42. # Le plan de construction
  43. La seule chose dont on a besoin, c'est le mot-clé `class` et un nom.
  44. ```python
  45. class MyObject:
  46. pas
  47. ```
  48. La classe est le plan de construction de notre objet.
  49. # Créons des objets
  50. ```python
  51. >>> object_1 = MyObject()
  52. >>> object_2 = MyObject()
  53. ```
  54. Python ne sait rien de l'objet à part son adresse on mémoire et son nom:
  55. ```python
  56. print(object_1)
  57. <__main__.MyObject at 0x7f52c831e2b0>
  58. ```
  59. On appelle `object_1` et `object_2` des *instances* de la classe `MyObject`.
  60. # Méthodes
  61. Une fonction dans une classe
  62. ```
  63. class MyObject:
  64. def my_method(the_object):
  65. print("hello", the_object)
  66. ```
  67. C'est tout!
  68. # Méthodes - 2
  69. La méthode n'existe pas en dehors de la classe (souvenez vous des cellules !)
  70. ```
  71. >>> my_method()
  72. NameError
  73. >>> object = MyObject()
  74. >>> object.my_method()
  75. Hello, <MyObject at 0x7f52c9f6d6d8>
  76. ```
  77. Notez que `my_method` a pris en premier argument ce qu'il y avait *à gauche* du point:
  78. D'ailleurs, ce code fonctionne aussi et retourne *la même chose*:
  79. ```
  80. >>> MyObject.my_method(object)
  81. Hello, <MyObject at 0x7f52c9f6d6d8>
  82. ```
  83. # Méthodes - 3
  84. Il *faut* passer l'objet en cours en paramètre:
  85. ```python
  86. class MyObject:
  87. def broken():
  88. print("You cannot call me!")
  89. ```
  90. ```python
  91. >>> o = MyObject()
  92. >>> o.broken()
  93. TypeError: broken() takes 0 positional arguments but 1 was given
  94. ```
  95. # Conventions
  96. * Les classes sont en CamelCase
  97. * Tout le reste (méthodes, etc...) en snake_case
  98. * L'objet en cours s'appelle **toujours** `self`
  99. ```python
  100. class MyObject:
  101. def my_method(self):
  102. print("hello", self)
  103. ```
  104. # Attributs
  105. * Des variables dans un objet.
  106. * On peut ajouter un attribut quand on veut à qui on veut, et toujours avec le
  107. point au milieu:
  108. ```python
  109. >>> object = MyObject()
  110. >>> object.attribute # ici l'attribut n'existe pas
  111. AttributError
  112. >>> object.attribute = 42 # maintenant oui
  113. >>> object.attribute
  114. 42
  115. ```
  116. # Attributs dans les méthodes
  117. Avec `self`, bien sûr:
  118. ```python
  119. class MyObject:
  120. def print_attribute(self):
  121. print(self.attribute)
  122. def change_attribute(self, new_value)
  123. self.attribute = new_value
  124. ```
  125. # Accéder aux attributs
  126. ```python
  127. >>> object = MyObject()
  128. >>> object.print_attribute() # ici l'attribut n'existe pas
  129. AttributError
  130. >>> object.attribute = 42
  131. >>> object.print_attribute() # ça marche
  132. 42
  133. >>> object.change_attribute(43)
  134. >>> object.attribute
  135. 43
  136. ```
  137. # Initialisation des attributs
  138. Avec `__init__`:
  139. * méthode "spéciale"
  140. * appelée automatiquement
  141. * notez les deux underscores avant et après ('dunder' en Anglais)
  142. ```python
  143. class MyObject:
  144. def __init__(self):
  145. self.attribute = 42
  146. ```
  147. ```python
  148. >>> object = MyObject()
  149. >>> object.attribute
  150. 42
  151. ```
  152. # Construire des objets différents
  153. `__init__()` et `MyObject()` sont des appels de fonctions comme les autres
  154. ```python
  155. class Car:
  156. def __init__(self, color_to_use="black"):
  157. self.color = color_to_use
  158. >>> ford = Car()
  159. >>> ford.color
  160. "black"
  161. >>> ferrari = Car(color_to_use="red")
  162. >>> ferrari.color
  163. "red"
  164. ```
  165. # Notes
  166. En vrai, on nomme souvent les paramètres du constructeur et les attributes de la même façon.
  167. ```python
  168. class Car:
  169. def __init__(self, color="black"):
  170. self.color = color
  171. ```
  172. # Récapitulatif
  173. * Classe: plan de construction
  174. * Object: ce qu'on crée avec le plan
  175. * Instance: objet issue d'une classe
  176. * Méthode: fonction dans une classe (qui prend `self` en premier argument)
  177. * Attribut: variable dans un objet
  178. #
  179. \center \huge Modules
  180. # Un fichier = un module
  181. Et oui, vous faites des modules sans le savoir depuis le début :)
  182. Un fichier `foo.py` correspond au module `foo`
  183. # Attention
  184. C'est pas tout à fait réciproque. Le module `foo` peut venir d'autre chose
  185. qu'un fichier.
  186. \vfill
  187. On y reviendra.
  188. # Importer un module
  189. Ou: accéder à du code provenant d'un *autre* fichier source.
  190. ```python
  191. # Dans foo.py
  192. a = 42
  193. ```
  194. \vfill
  195. ```python
  196. # Dans bar.py
  197. import foo
  198. print(foo.a)
  199. ```
  200. \vfill
  201. * Affiche '42'
  202. # Espaces de noms
  203. On dit aussi *namespace*.
  204. Du point de vue de `bar.py`, `a` est dans l'*espace de nom* foo.
  205. * On retrouve la syntaxe pour accèder à un attribut: `<variable>.<membre>`.
  206. (ce n'est pas un hasard)
  207. * Les namespaces sont automatiques en Python
  208. # Importer dans le REPL
  209. ```python
  210. # dans foo.py
  211. def ma_fonction():
  212. return 42
  213. ```
  214. ```python
  215. # dans le REPL
  216. >>> import foo
  217. >>> foo.ma_fonction()
  218. 42
  219. ```
  220. * Plus sympa que de rajouter des `print()` à la fin de `foo.py` ;)
  221. # Les imports ne sont faits qu'une seule fois
  222. ```python
  223. # Dans foo.py
  224. print("Je suis le module foo et tu viens de m’importer")
  225. ```
  226. ```python
  227. >>> import foo
  228. Je suis le module foo et tu viens de m’importer
  229. >>> import foo
  230. <rien>
  231. ```
  232. On retrouve le concept de *cache*.
  233. # Attention
  234. Il faudra donc redémarrer le REPL à chaque fois que le code change.
  235. Parfois, les gens conseillent d'utiliser `reload()` mais cette fonction n'est pas toujours fiable :/
  236. # Un raccourci
  237. ```
  238. >>> from foo import *
  239. >>> ma_fonction()
  240. 42
  241. ```
  242. * Utile dans le REPL
  243. * Pas une très bonne idée dans du vrai code
  244. * On perd la trace de où vient la variable * Possibilité de collisions de noms
  245. # Retour sur les scripts
  246. Un script, par opposition à un module n'est pas censé être importé.
  247. Une solution est de mettre des tirets dans le nom:
  248. ```python
  249. # Dans foo.py
  250. import my-script
  251. ```
  252. * Essaye de soustraire `script` à `my`
  253. * ça ne fonctionne pas
  254. # Retour sur main()
  255. La méthode `main()` ne *doit* pas être exécutée quand on importe le code!
  256. Solution:
  257. ```python
  258. # Dans foo.py
  259. def my_function():
  260. # Fonction utile qui peut être ré-utilisée
  261. def main():
  262. # Fonction d'entrée principale
  263. # Utilise my_function()
  264. # magie!
  265. if __name__ == "__main__":
  266. main()
  267. ```
  268. # Explication (partielle) de la magie
  269. Le `if` n'est vrai *que* quand on lance `python3 foo.py`, mais pas quand on appelle `import foo` depuis
  270. un autre module.
  271. La variable magique `__name__` est égale au nom du module quand il est importé, et à `__main__` sinon.
  272. # La librarie standard (1)
  273. * Une collection de modules directement utilisables fournis à l'installation de Python.
  274. * on a déjà vu `sys`, pour `sys.exit()` ou `sys.argv`
  275. # La librarie standard (2)
  276. Toute la librarie standard est documentée - même en Français.
  277. https://docs.python.org/fr/3/library/index.html
  278. * Gardez-là sous votre oreiller :)
  279. # La librarie standard (3)
  280. Beacoup de choses dedans. (*batteries included*)
  281. De quoi faire de la manipulation de texte, des statistiques, du réseau, de la concurrence, etc ...
  282. #
  283. \center \huge Atelier
  284. # Jouons avec les API Web
  285. API web: un serveur avec qui on peut parler depuis un programme.
  286. Il en existe des quantités sur internet.
  287. Aujourd'hui on va utiliser `numbersapi.com`
  288. # Numbersapi
  289. Example: On fait une requête sur `http://numbersapi.com/42`, on récupère du texte
  290. contenant un fait intéressant (*trivia* en anglais) à propos du nombre 42 .
  291. # Squelette
  292. ```python
  293. import sys
  294. import urllib.request
  295. BASE_URL = "http://numbersapi.com/"
  296. def main():
  297. number = sys.argv[1]
  298. url = BASE_URL = number
  299. with urllib.request.urlopen(url) as request:
  300. response = request.read().decode("utf-8")
  301. print(response)
  302. if __name__ == "__main__":
  303. main()
  304. ```
  305. # Idée
  306. * Transformer ceci en une classe réutilisable
  307. * Gérer les autres fonctionnalités de numbersapi.com (dates, tirages aléatoires, etc ...)