25개 이상의 토픽을 선택하실 수 없습니다. 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-13.md 7.6 KiB

5 년 전
5 년 전
5 년 전
5 년 전
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. % Programmation avec Python (chapitre 13)
  2. % Dimitri Merejkowsky
  3. #
  4. \center \huge Rappels
  5. # Virtualenvs
  6. * Création:
  7. ```
  8. $ python3 -m venv /path/to/foo
  9. ```
  10. * Utilisation:
  11. ```
  12. $ /path/to/foo/bin/pip install requests
  13. # ou
  14. $ source /path/to/foo/bin/activate
  15. (foo) $ pip install requests
  16. ```
  17. # Tests
  18. ```python
  19. # foo.py
  20. def add_3(x):
  21. return x + 3
  22. # test_foo.py
  23. import foo
  24. def test_add_3():
  25. result = foo.add_3(2)
  26. assert result == 5
  27. ```
  28. # pytest
  29. ```
  30. $ pytest test_foo.py
  31. ============== test session starts =================
  32. test_foo.py . [100%]
  33. =========== 1 passed in 0.01 seconds ===============
  34. ```
  35. # TDD
  36. * Une discipline
  37. * 4 règles
  38. * Un cycle
  39. # Règles
  40. * Règle 1 : Il est interdit d'écrire du code de production, *sauf* si c'est pour faire passer un test qui
  41. a échoué.
  42. * Règle 2 : Il est interdit d'écrire plus de code que celui qui est nécessaire pour provoquer une erreur
  43. dans les tests (n'importe quelle erreur)
  44. * Règle 3 : Il est interdit d'écrire plus de code que celui qui est nécessaire pour faire passer
  45. un test qui a échoué
  46. * Règle 4 : Une fois que tous les tests passent, il est interdit de modifier le code sans s'arrêter
  47. pour considérer la possibilité d'un refactoring.
  48. # Cycle
  49. * RED: on écrit un test qui échoue
  50. * GREEN: on fait passer le test
  51. * REFACTOR: on refactor le code de production et le code de test
  52. #
  53. \center \huge Générateurs
  54. \vfill
  55. \normalsize Ces objects qui sont "presque" des listes ...
  56. # Retour sur les boucles
  57. Itération sur une liste
  58. ```python
  59. for i in [1, 2, 3]:
  60. print(i)
  61. ```
  62. Clés d'un dictionnaire
  63. ```python
  64. for key in {"a": 1, "b": 2, "c": 3}.keys():
  65. print(key)
  66. ```
  67. Valeurs d'un dictionnaire
  68. ```python
  69. for value in {"a": 1, "b": 2, "c": 3}.values():
  70. print(key)
  71. ```
  72. # Pas des listes
  73. ```python
  74. >>> my_dict = {"a": 1, "b": 2, "c": 3}
  75. >>> my_dict.keys()
  76. dict_keys(['a', 'b', 'c'])
  77. >>> my_dict.keys()[0]
  78. TypeError: 'dict_keys' object is not subscriptable
  79. ```
  80. # Vues
  81. En fait, `.keys()`, `.values()`, `.items()` renvoient des *vues*.
  82. Elles changent quand le dictionnaire changent, mais on ne peut pas
  83. s'en servir pour changer la taille du dictionnaire.
  84. ```python
  85. >>> my_dict = {"a": 1, "b": 2, "c": 3}
  86. >>> for k in my_dict.keys():
  87. >>> if k == "a":
  88. >>> del my_dict[k]
  89. RuntimeError: dictionary changed size during iteration
  90. ```
  91. # Vues (2)
  92. Si vraiment vous en avez besoin, vous pouver les convertir en liste
  93. avec `list()`.
  94. ```python
  95. >>> my_dict = {"a": 1, "b": 2, "c": 3}
  96. >>> for k in list(my_dict.keys()):
  97. >>> if k == "a":
  98. >>> del my_dict[k]
  99. >>> my_dict
  100. {"b": 2, "c": 3}
  101. ```
  102. # Listes par compréhension
  103. ```python
  104. # 1
  105. sequence = ["a", "b", "c"]
  106. new_sequence = []
  107. for element in sequence:
  108. new_sequence.append(element.upper())
  109. # 2
  110. sequence = ["a", "b", "c"]
  111. new_sequence = [element.upper() for element in sequence]
  112. ```
  113. # Listes par compréhension (2)
  114. On peut transformer la séquence originale:
  115. ```python
  116. >>> sequence = [element.upper() for element in sequence]
  117. >>> sequence
  118. ["A", "B", "C"]
  119. ```
  120. \vfill
  121. On peut changer le type:
  122. ```python
  123. >>> as_strings = ["1", "2", "3"]
  124. >>> numbers = [int(x) for x in as_strings]
  125. >>> numbers
  126. [1, 2, 3]
  127. ```
  128. # Filtrer une liste
  129. ```python
  130. >>> numbers = [1, 2, 3, 4]
  131. >>> odds = [x for x in numbers if x % 2 == 1]
  132. >>> odds
  133. [1, 3]
  134. ```
  135. \vfill
  136. ```python
  137. >>> numbers = [1, 2, 3, 4]
  138. >>> odds_squared = [x*x for x in numbers if x % 2 == 1]
  139. >>> odds_squared
  140. [1, 9]
  141. ```
  142. # Dictionnaires et ensembles par compréhension
  143. Même principe:
  144. ```python
  145. >>> scores = [("bob", 2), ("alice", 3)]
  146. >>> my_dict = {t[0]:t[1] for t in scores}
  147. >>> my_dict
  148. {"bob": 2, "alice": 3}
  149. ```
  150. \vfill
  151. ```python
  152. >>> numbers = [-1, 2, -3, 3]
  153. >>> numbers = {abs(x) for x in numbers}
  154. >>> numbers
  155. {1, 2, 3}
  156. ```
  157. # Avantage des compréhensions
  158. * Code plus succint
  159. * Code plus rapide :)
  160. * Code "idiomatique"
  161. # Apparté: Python est gourmand
  162. Python évalue d'habitude de manière "gourmande":
  163. ```python
  164. def foo():
  165. ..
  166. def bar():
  167. ...
  168. x = foo(bar(y))
  169. ```
  170. On calcule d'abord `y`, puis `bar(y)` puis `foo(bar(y))`
  171. # Générateurs
  172. Concept: fournir les valeurs une par une, à la demande.
  173. Le code *à l'intérieur* des compréhensions est un générateur.
  174. On peut le voir si on utilise des parenthèses:
  175. ```python
  176. >>> generator = (x for x in range(0, 3))
  177. >>> generator
  178. <generator object <genexpr> at 0x7f654c272138>
  179. >>> list(generator)
  180. [0, 1, 2]
  181. ```
  182. Les générateurs sont "feignants": ils ne calculent leur valeurs que quand
  183. c'est demandé
  184. # Les générateurs s'épuisent
  185. ```python
  186. >>> generator = (x for x in range(0, 3))
  187. >>> list(generator)
  188. [0, 1, 2]
  189. >>> list(generator)
  190. []
  191. ```
  192. # next()
  193. On peut aussi appeler `next()` sur un générateur, au lieu de `for ... in`
  194. ```python
  195. >>> generator = (x for x in range(0, 2))
  196. >>> next(generator)
  197. 0
  198. >>> next(generator)
  199. 1
  200. >>> next(generator)
  201. StopIteration
  202. ```
  203. # Fabriquer un générateur à l'aide d'une classe
  204. Avec une méthode `__next__()`
  205. ```python
  206. class Squares:
  207. def __init__(self):
  208. self.value = 0
  209. def __next__(self):
  210. self.value += 1
  211. return self.value ** 2
  212. ```
  213. ```python
  214. >>> squares = Squares()
  215. >>> next(squares)
  216. 1
  217. >>> next(squares)
  218. 4
  219. >>> next(squares)
  220. 9
  221. ```
  222. # Les générateurs ne sont pas des itérateurs
  223. ```python
  224. >>> [x for x in s if x < 10]
  225. TypeError: 'Squares' object is not iterable
  226. ```
  227. Pour faire `for in`, il faut un itérateur, en plus d'un générateur
  228. # Fabriquer un itérateur à l'aide d'une classe
  229. Avec `__iter__` *et* `__next__`:
  230. ```python
  231. class SquaresLessThan:
  232. def __init__(self, stop_value):
  233. self.value = 0
  234. self.stop_value = stop_value
  235. def __iter__(self):
  236. return self
  237. def __next__(self):
  238. self.value += 1
  239. res = self.value ** 2
  240. if res > self.stop_value:
  241. raise StopIteration()
  242. return res
  243. ```
  244. # Fabriquer un itérateur à l'aide d'une classe
  245. ```python
  246. >>> squares = SquaresLessThan(100)
  247. >>> [x for x in squares]
  248. [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
  249. ```
  250. On retrouve les propriétés des expressions génératrices:
  251. ```python
  252. >>> squares = SquaresLessThan(100)
  253. >>> squares[2]
  254. TypeError: 'SquaresLessThan' object is not subscriptable
  255. >>> squares = SquaresLessThan(100)
  256. >>> list(squares)
  257. [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
  258. >>> list(squares)
  259. []
  260. ```
  261. # yield
  262. On peut aussi faire des générateurs avec `yield`
  263. pour mettre le générateur "en pause", et `return` pour
  264. terminer:
  265. ```python
  266. def squares(max_value):
  267. x = 1
  268. res = 1
  269. while res <= max_value:
  270. yield res
  271. x += 1
  272. res = x * x
  273. return
  274. >>> s = squares(100)
  275. >>> [x for x in s]
  276. [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
  277. ```
  278. # Combiner avec une classe génératrices
  279. ```python
  280. class Squares:
  281. def __init__(self):
  282. self.value = 0
  283. def __next__(self):
  284. self.value += 1
  285. return self.value ** 2
  286. def squares_less_than(max_value):
  287. squares = Squares()
  288. while True:
  289. x = next(squares)
  290. if x > max_value:
  291. return
  292. yield x
  293. ```
  294. # yield from
  295. On peut aussi chaîner les fonctions génératrices
  296. ```python
  297. def integers(max_value):
  298. x = 0
  299. while x < max_value:
  300. yield x
  301. x += 1
  302. return
  303. def zero_to_five():
  304. yield from integers(5)
  305. >>> s = zero_to_five()
  306. >>> [x for x in s]
  307. [0, 1, 2, 3, 4]
  308. ```
  309. #
  310. \center \huge Consignes
  311. #
  312. * Repartir de git.e2li.org/dmerejkowsky/cours-python/sources/lister
  313. * Ajouter le code pour gérer correctement l'attribut <is_directory> de l
  314. class Entry
  315. * Utiliser TDD pour la suite!
  316. * Faire en sorte d'afficher les répertoires avec un `/` à la fin
  317. * Afficher la date de modification de manière plus lisible
  318. (voir `time` dans la bibliothèque standard)
  319. * Grouper les éléments sur plusieurs colonnes
  320. * Implémenter d'autres options (`--sort=time`, etc.)
  321. * Réécrire parse_args() en utilisant `argparse`.
  322. # Solutions et questions
  323. Envoyez-moi un email à dimitri@e2li.org !