Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
Tento repozitář je archivovaný. Můžete prohlížet soubory, klonovat, ale nemůžete nahrávat a vytvářet nové úkoly a požadavky na natažení.

python-06.md 10 KiB

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