您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
此仓库已存档。您可以查看文件和克隆,但不能推送或创建工单/合并请求。

python-06.md 9.4 KiB

5 年前
5 年前
5 年前
5 年前
5 年前
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  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. pas
  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. >>> print(object_2)
  73. <__main__.MyObject at 0x7fb39e5ac748>
  74. ```
  75. Une autre adresse mémoire, donc un objet différent.
  76. # Méthodes
  77. Une fonction dans une classe
  78. ```
  79. class MyObject:
  80. def my_method(the_object):
  81. print("hello", the_object)
  82. ```
  83. C'est tout!
  84. # Méthodes - 2
  85. La méthode n'existe pas en dehors de la classe - souvenez vous des cellules !
  86. ```
  87. >>> my_method()
  88. NameError
  89. >>> object = MyObject()
  90. >>> object.my_method()
  91. Hello, <MyObject at 0x7f52c9f6d6d8>
  92. ```
  93. # Méthodes - 2
  94. ```
  95. >>> object.my_method()
  96. Hello, <MyObject at 0x7f52c9f6d6d8>
  97. ```
  98. Notez que `my_method` a pris en premier argument ce qu'il y avait *à gauche* du point.
  99. D'ailleurs, ce code fonctionne aussi et retourne *la même chose*:
  100. ```
  101. >>> MyObject.my_method(object)
  102. Hello, <MyObject at 0x7f52c9f6d6d8>
  103. ```
  104. # Méthodes - 3
  105. Il *faut* passer l'objet en cours en paramètre:
  106. ```python
  107. class MyObject:
  108. def broken():
  109. print("You cannot call me!")
  110. ```
  111. ```python
  112. >>> o = MyObject()
  113. >>> o.broken()
  114. TypeError: broken() takes 0 positional arguments
  115. but 1 was given
  116. ```
  117. # Conventions
  118. * Les classes sont en CamelCase
  119. * Tout le reste (méthodes, etc...) en snake_case
  120. * L'objet en cours s'appelle **toujours** `self`
  121. ```python
  122. class MyObject:
  123. def my_method(self):
  124. print("hello", self)
  125. ```
  126. # Attributs
  127. * Des variables dans un objet.
  128. * On peut ajouter un attribut quand on veut à qui on veut, et toujours avec le
  129. point au milieu:
  130. ```python
  131. >>> object = MyObject()
  132. >>> object.attribute # ici l'attribut n'existe pas
  133. AttributError
  134. >>> object.attribute = 42 # maintenant oui
  135. >>> object.attribute
  136. 42
  137. ```
  138. # Attributs dans les méthodes
  139. Avec `self`, bien sûr:
  140. ```python
  141. class MyObject:
  142. def print_attribute(self):
  143. print(self.attribute)
  144. def change_attribute(self, new_value)
  145. self.attribute = new_value
  146. ```
  147. # Accéder aux attributs
  148. ```python
  149. >>> object = MyObject()
  150. >>> object.print_attribute() # ici l'attribut n'existe pas
  151. AttributError
  152. >>> object.attribute = 42
  153. >>> object.print_attribute() # ça marche
  154. 42
  155. >>> object.change_attribute(43)
  156. >>> object.attribute
  157. 43
  158. ```
  159. # Initialisation des attributs
  160. Avec `__init__`:
  161. * méthode "spéciale"
  162. * appelée automatiquement
  163. * notez les deux underscores avant et après ('dunder' en anglais)
  164. ```python
  165. class MyObject:
  166. def __init__(self):
  167. self.attribute = 42
  168. ```
  169. ```python
  170. >>> object = MyObject()
  171. >>> object.attribute
  172. 42
  173. ```
  174. # Construire des objets différents
  175. `__init__()` et `MyObject()` sont des appels de fonctions comme les autres
  176. ```python
  177. class Car:
  178. def __init__(self, color_to_use="black"):
  179. self.color = color_to_use
  180. >>> ford = Car()
  181. >>> ford.color
  182. "black"
  183. >>> ferrari = Car(color_to_use="red")
  184. >>> ferrari.color
  185. "red"
  186. ```
  187. # Notes
  188. En vrai, on nomme souvent les paramètres du constructeur et les attributes de la même façon.
  189. ```python
  190. class Car:
  191. def __init__(self, color="black"):
  192. self.color = color
  193. ```
  194. # Récapitulatif
  195. * Classe: plan de construction
  196. * Object: ce qu'on crée avec le plan
  197. * Instance: objet issue d'une classe
  198. * Méthode: fonction dans une classe (qui prend `self` en premier argument)
  199. * Attribut: variable dans un objet
  200. #
  201. \center \huge Modules
  202. # Un fichier = un module
  203. Et oui, vous faites des modules sans le savoir depuis le début :)
  204. Un fichier `foo.py` correspond au module `foo`
  205. # Attention
  206. C'est pas tout à fait réciproque. Le module `foo` peut venir d'autre chose
  207. qu'un fichier.
  208. # Importer un module
  209. Ou: accéder à du code provenant d'un *autre* fichier source.
  210. ```python
  211. # Dans foo.py
  212. a = 42
  213. ```
  214. \vfill
  215. ```python
  216. # Dans bar.py
  217. import foo
  218. print(foo.a)
  219. ```
  220. \vfill
  221. * Affiche '42'
  222. # Espaces de noms
  223. On dit aussi *namespace*.
  224. Du point de vue de `bar.py`, `a` est dans l'*espace de nom* foo.
  225. * On retrouve la syntaxe pour accèder à un attribut: `<variable>.<membre>`.
  226. (ce n'est pas un hasard)
  227. * Les namespaces sont automatiques en Python
  228. # Importer dans le REPL
  229. ```python
  230. # dans foo.py
  231. def ma_fonction():
  232. return 42
  233. ```
  234. ```python
  235. # dans le REPL
  236. >>> import foo
  237. >>> foo.ma_fonction()
  238. 42
  239. ```
  240. * Plus sympa que de rajouter des `print()` à la fin de `foo.py` ;)
  241. # Les imports ne sont faits qu'une seule fois
  242. ```python
  243. # Dans foo.py
  244. print("Je suis le module foo et tu viens de m’importer")
  245. ```
  246. ```python
  247. >>> import foo
  248. Je suis le module foo et tu viens de m’importer
  249. >>> import foo
  250. <rien>
  251. ```
  252. On retrouve le concept de *cache*.
  253. # Attention
  254. Il faudra donc redémarrer le REPL à chaque fois que le code change.
  255. Parfois, les gens conseillent d'utiliser `reload()` mais cette fonction n'est pas toujours fiable :/
  256. # Un raccourci
  257. ```
  258. >>> from foo import *
  259. >>> ma_fonction()
  260. 42
  261. ```
  262. * Utile dans le REPL
  263. * Pas une très bonne idée dans du vrai code
  264. * On perd la trace de où vient la variable
  265. * Possibilité de collisions de noms
  266. # Retour sur les scripts
  267. Un script, par opposition à un module n'est pas censé être importé.
  268. Une solution est de mettre des tirets dans le nom:
  269. ```python
  270. # Dans foo.py
  271. import my-script
  272. ```
  273. * Essaye de soustraire `script` à `my`
  274. * ça ne fonctionne pas
  275. # Retour sur main()
  276. La méthode `main()` ne *doit* pas être exécutée quand on importe le code!
  277. Solution:
  278. ```python
  279. # Dans foo.py
  280. def my_function():
  281. # Fonction utile qui peut être ré-utilisée
  282. def main():
  283. # Fonction d'entrée principale
  284. # Utilise my_function()
  285. # magie!
  286. if __name__ == "__main__":
  287. main()
  288. ```
  289. # Explication (partielle) de la magie
  290. Le `if` n'est vrai *que* quand on lance `python3 foo.py`, mais pas quand on appelle `import foo` depuis
  291. un autre module.
  292. La variable magique `__name__` est égale au nom du module quand il est importé, et à la string `"__main__"` sinon.
  293. # La librarie standard (1)
  294. * Une collection de modules directement utilisables fournis à l'installation de Python.
  295. * on a déjà vu `sys`, pour `sys.exit()` ou `sys.argv`
  296. # La librarie standard (2)
  297. Toute la librarie standard est documentée - même en Français.
  298. https://docs.python.org/fr/3/library/index.html
  299. * Gardez-là sous votre oreiller :)
  300. # La librarie standard (3)
  301. Beacoup de choses dedans. (*batteries included*)
  302. De quoi faire de la manipulation de texte, des statistiques, du réseau, de la concurrence, etc ...
  303. #
  304. \center \huge Atelier
  305. # Jouons avec les API Web
  306. API web: un serveur avec qui on peut parler depuis un programme.
  307. Il en existe des quantités sur internet.
  308. Aujourd'hui on va utiliser `numbersapi.com`
  309. # Numbersapi
  310. Example: On fait une requête sur `http://numbersapi.com/42`, on récupère du texte
  311. contenant un fait intéressant (*trivia* en anglais) à propos du nombre 42 .
  312. # Squelette
  313. ```python
  314. import sys
  315. import urllib.request
  316. BASE_URL = "http://numbersapi.com/"
  317. def main():
  318. number = sys.argv[1]
  319. url = BASE_URL = number
  320. with urllib.request.urlopen(url) as request:
  321. response = request.read().decode("utf-8")
  322. print(response)
  323. if __name__ == "__main__":
  324. main()
  325. ```
  326. # Idée
  327. * Transformer ceci en une classe réutilisable
  328. * Gérer les autres fonctionnalités de numbersapi.com (dates, tirages aléatoires, etc ...)