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-03.md 11 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667
  1. % Programmation avec Python (chapitre 3)
  2. % Dimitri Merejkowsky
  3. \center \huge None
  4. # Jouons avec les dictionnaires
  5. ```python
  6. >>> scores = { "Anne": 42, "Bernard": 5 }
  7. >>> scores["Anne"]
  8. 42
  9. >>> scores.get("Anne")
  10. 42
  11. >>> scores["Sophie"]
  12. KeyError
  13. >>> scores.get("Sophie")
  14. <rien>
  15. ```
  16. Que se passe-t-il?
  17. # Exprimer l'absence
  18. En réalité, `get()` retourne None quand la clé n'est pas présente
  19. \vfill
  20. ```python
  21. >>> a = 42
  22. >>> a
  23. 42
  24. >>> b = None
  25. >>> b
  26. <rien>
  27. ```
  28. # None est ambigu
  29. None est `falsy`, tout comme 0, False et les listes vides:
  30. ```python
  31. element = dictionnaire.get(clé)
  32. if not element:
  33. ...
  34. ```
  35. Mais ici, comment faire la différence entre:
  36. * la clé existe et vaut None
  37. * la clé existe et est falsy
  38. * la clé n'existe pas ?
  39. # Solution 1: tester l'appartenance
  40. Avec `in`:
  41. ```python
  42. if clé in dictionnaire:
  43. # La clé existe, pas d'erreur
  44. valeur = dictionnaire[clé]
  45. ```
  46. # Solution 2: tester None
  47. Avec `is`:
  48. ```python
  49. >>> scores = { "Anne": 42, "Bernard": 5 }
  50. >>> score1 = scores.get("Anne")
  51. >>> score1 is None
  52. False
  53. >>> score2 = scores.get("Sophie")
  54. >>> score2 is None
  55. True
  56. ```
  57. # Retourner None
  58. `None` est aussi la valeur par défaut lorsqu'il n'y a pas de `return`
  59. dans le corps de la fonction:
  60. ```python
  61. >>> def ne_retourne_rien(a, b):
  62. >>> c = a + b
  63. >>> résultat = ne_retourne_rien(3, 2)
  64. >>> résultat is None
  65. True
  66. ```
  67. # Retourner None (2)
  68. On dit aussi que la fonction est une *procédure*.
  69. On a déjà écrit des procédures: dans `pendu.py`, `display_hint()` et `main()`
  70. ne retournaient rien.
  71. # Retourner None au autre chose
  72. ```python
  73. def trouve_dans_liste1(liste, element):
  74. if element in list:
  75. return liste.index(element)
  76. def trouve_dans_liste2(liste, element):
  77. if element in list:
  78. return liste.index(element)
  79. else:
  80. return None
  81. ```
  82. \vfill
  83. Les deux fonctions font la même chose : `trouve_dans_liste2` est simplement plus *explicite.*
  84. # Types optionnels
  85. ```python
  86. def trouve_dans_liste(liste, element):
  87. if element in list:
  88. return liste.index(element)
  89. else:
  90. return None
  91. ```
  92. On dit aussi que le type de retour de `trouve_dans_liste` est *optionnel*.
  93. #
  94. \centering \huge Retour sur les listes
  95. # Méthodes
  96. ```python
  97. fruits = ["pomme", "banane"]
  98. fruits.append("orange")
  99. ```
  100. Quand on écrit `fruits.append("orange")`, on peut voir `append()`
  101. comme une "fonction" qui prendrait `fruits` en argument implicite
  102. (avant le `.`), et `orange` en argument explicite.
  103. On appelle ce genre de fonction une **méthode**.
  104. # clear()
  105. Pour vider une liste:
  106. ```python
  107. >>> fruits = ["pomme", "banane"]
  108. >>> fruits.clear()
  109. >>> fruits
  110. []
  111. ```
  112. Notez que la méthode `clear()` ne renvoie rien!
  113. La liste est modifiée *sur place*.
  114. # extend()
  115. Pour concaténer des listes:
  116. ```python
  117. >>> fruits = ["pomme", "banane"]
  118. >>> fruits.extend(["orange", "abricot"])
  119. >>> fruits
  120. ['pomme', 'banane', 'orange', 'abricot']
  121. ```
  122. \vfill
  123. Peut aussi s'écrire `+=`
  124. ```python
  125. >>> nombres = [1, 2]
  126. >>> nombres += [3, 4]
  127. >>> nombres
  128. [1, 2, 3, 4]
  129. ```
  130. # pop()
  131. On a vu la méthode `pop()` pour les dictionnaires, mais elle existe
  132. aussi pour les listes:
  133. \vfill
  134. ```python
  135. >>> fruits = ["pomme", "banane"]
  136. >>> fruits.pop() # Retourne l'élément
  137. 'pomme'
  138. >>> fruits # Et modifie la liste
  139. ["banane"]
  140. ```
  141. # pop() sur liste vide
  142. ```python
  143. >>> liste_vide = list()
  144. >>> liste_vide.pop()
  145. IndexError
  146. ```
  147. # index()
  148. Pour récupérer la position d'un élément:
  149. ```python
  150. >>> fruits = ["pomme", "banane"]
  151. >>> fruits.index("banane")
  152. >>> 1
  153. >>> fruits.index("citron")
  154. >> ValueError
  155. ```
  156. # count()
  157. Pour compter le nombre d'occurrences d'un élément
  158. ```python
  159. >>> fruits = ["pomme", "banane", "pomme", "poire"]
  160. >>> fruits.count("pomme")
  161. 2
  162. >>> fruits.count("poire")
  163. 1
  164. >>> fruits.count("citron")
  165. 0
  166. ```
  167. # Indexer des listes
  168. Rappel:
  169. ```python
  170. >>> lettres = ["a", "b", "c", "d", "e"]
  171. >>> lettres[0] # ça commence à zéro
  172. "a"
  173. >>> lettres[4]
  174. "e"
  175. ```
  176. Mais on peut aussi compter à l'envers:
  177. ```python
  178. >>> lettres[-1]
  179. "e"
  180. >>> lettres[-2]
  181. "d"
  182. ```
  183. # Trancher des listes
  184. Ou "faire des slices", ou "slicer".
  185. ```python
  186. >>> lettres = ["a", "b", "c", "d", "e"]
  187. >>> lettres[1:3] # début (inclus), fin (non-inclus)
  188. ['b', 'c']
  189. >>> lettres[:3] # début implicite
  190. ['a', 'b', 'c']
  191. >>> lettres[3:] # fin implicite
  192. ['d', 'e']
  193. >>> lettres[1:-2] # fin négative
  194. ['b', 'c']
  195. ```
  196. #
  197. \centering \huge Retour sur les strings
  198. # Rappel
  199. On a vu que les strings étaient "presque" des liste de caractères.
  200. Par exemple, on peut itérer sur les lettres d'un mot avec: `for lettre
  201. in mot`.
  202. # index() et count() marchent aussi
  203. ```python
  204. >>> message = "Bonjour, monde !"
  205. >>> message.index("B")
  206. 0
  207. >>> message.count("o")
  208. 3
  209. ```
  210. # Trancher des chaînes de caractères
  211. On peu aussi slicer des strings:
  212. \vfill
  213. ```python
  214. >>> message = "Bonjour, monde !"
  215. >>> message[1:4]
  216. 'onj'
  217. >>> message[:7]
  218. 'Bonjour'
  219. >>> message[9:-2]
  220. 'monde'
  221. ```
  222. # Les strings sont immuables
  223. Mais on ne **peut pas** modifier une string "sur place".
  224. ```python
  225. >>> message = "Bonjour, monde !"
  226. >>> message[-1] = "?"
  227. TypeError
  228. ```
  229. ```python
  230. >>> message = "Bonjour, monde !"
  231. >>> message.clear()
  232. AttributeError
  233. ```
  234. #
  235. \centering \huge La ligne de commande
  236. # Schéma
  237. ![programme](img/programme.png)
  238. # Entrée / sortie
  239. 3 canaux:
  240. * Une entrée qu'on peut lire (`stdin`)
  241. * Deux sorties:
  242. * Sortie normale (ou standard) (`stdout`)
  243. * Sortie d'erreur (`stdout`)
  244. # Accès en Python
  245. * Pour lire stdin: `input()`
  246. * Pour écrire dans stdout: `print()`
  247. * Pour écrire dans stderr: pas de fonction native
  248. # Accès en Python (2)
  249. Rajouter `import sys` en haut du fichier, puis:
  250. * `sys.stdin.read()`
  251. * `sys.stdout.write()`
  252. * `sys.stderr.write()`
  253. On peut aussi utiliser:
  254. `print("erreur", file=sys.stderr)`
  255. # Accès depuis l'invite de commande
  256. On dit aussi *shell*, ou *CLI* (pour *command line interface*).
  257. * stdin: Taper quand le shell vous laisse la main, finir par 'entrée'
  258. * stdout et stderr sont affichés en même temps pas défaut
  259. # Rediriger stdout
  260. Linux , macOS, Windows:
  261. ```
  262. python3 fichier.py > output.txt
  263. ```
  264. stdout sera écrit dans `output.txt`, et seul `stderr` sera visible.
  265. # Code de retour
  266. * 0 quand tout va bien
  267. * un autre entier quand quelque chose va mal
  268. \vfill
  269. > Toutes les familles heureuses se ressemblent, mais chaque famille
  270. > malheureuse l'est à sa façon.
  271. >
  272. > Tolstoï
  273. # Code de retour
  274. Les valeurs d'erreur possibles sont en général présentes
  275. dans la documentation.
  276. Note: **ne pas retourner 0** en cas d'erreur, même minime, et même si
  277. un message a été affiché.
  278. C'est important pour pourvoir composer plusieurs programmes (on y
  279. reviendra).
  280. # Afficher le code retour depuis le shell
  281. ```bash
  282. # Linux, macOS
  283. $ python3 code.py
  284. $ echo $?
  285. 0
  286. ```
  287. ```bash
  288. # Windows
  289. > python3 code.py
  290. > echo %ERRORLEVEL%
  291. 0
  292. ```
  293. # Gestion du code de retour en Python
  294. Par défaut, le code de retour est 0.
  295. On peut terminer le programme avec `sys.exit()` et un numéro:
  296. ```python
  297. import sys
  298. def fait_quelque_chose():
  299. if erreur:
  300. sys.exit(1)
  301. ```
  302. Note: dans un vrai programme, veillez à construire et afficher un
  303. message utile!
  304. # Gestion du code de retour en Python (2)
  305. `sys.exit(0)` pour terminer immédiatement et sans erreur.
  306. ```python
  307. import sys
  308. def antivirus():
  309. problèmes = cherche_problèmes()
  310. if not problèmes:
  311. print("Aucun problème trouvé")
  312. sys.exit(0)
  313. for problème in problèmes:
  314. # traite chaque problème un à un
  315. ...
  316. ````
  317. # Gestion du code de retour en Python - un raccourci
  318. On peut passer le message d'erreur directement à `sys.exit()` avec
  319. une string au lieu d'un numéro:
  320. ```python
  321. if erreur:
  322. sys.exit("Une erreur est survenue")
  323. ```
  324. # Les arguments d'un programme
  325. Pour lancer un programme, on tape son nom, suivi de mots séparés pas
  326. des espaces.
  327. En Python, ça donne
  328. ```
  329. python3 fichier.py arg1 arg2
  330. ```
  331. # Accès aux arguments en Python
  332. * `import sys`
  333. * `sys.argv`
  334. `sys.argv` est une liste, et son premier argument est
  335. *toujours* le nom du fichier exécuté.
  336. # Exemple
  337. ```python
  338. # Dans foo.py
  339. import sys
  340. print("Fichier source:", sys.argv[0])
  341. print("Reste des arguments:", sys.argv[1:])
  342. ```
  343. \vfill
  344. ```
  345. $ python3 foo.py un deux
  346. Fichier source: foo.py
  347. Reste des arguments: ['un', 'deux']
  348. ```
  349. #
  350. \center \huge Cas pratique
  351. # Squelette
  352. Décodeur de noms d'aéroports
  353. * Lire un fichier avec les codes et les noms associés
  354. * En faire un dictionnaire
  355. * Utiliser le premier argument comme nom de code
  356. * Afficher le nom complet de l'aéeroport, ou une
  357. erreur s'il est inconnu.
  358. # Différentes approches
  359. * Bottom-up (approche *ascendante*)
  360. * Top-Down (approche *descendante*)
  361. # Approches bottom-up
  362. Utilisé pour le pendu:
  363. * construire des blocs élémentaires (les petites fonctions `has_won`,
  364. `display_hint`, etc...)
  365. * les assembler pour faire un tout plus "haut niveau" (le corps de la
  366. fonction `main()`
  367. # Approche top-down
  368. Essayons de partir du plus "haut niveau" et de "descendre" vers les
  369. blocs élémentaires
  370. # Code
  371. ```python
  372. def main():
  373. dico = fabrique_dico()
  374. code = lire_code()
  375. nom = trouve_code(code, dico)
  376. if nom:
  377. print(nom)
  378. else:
  379. affiche_erreur(code)
  380. main()
  381. ```
  382. On a fait comme si le code dont on avait besoin était déjà écrit :)
  383. # lire_code()
  384. ```python
  385. import sys
  386. def lire_code():
  387. if len(sys.argv) < 2:
  388. print(
  389. "Pas assez d'arguments",
  390. file=sys.stderr
  391. )
  392. sys.exit(1)
  393. argument = sys.argv[1]
  394. # On accepte `cdg` ou `CDG`.
  395. code = argument.upper()
  396. return code
  397. ```
  398. # fabrique_dico()
  399. Le fichier `airports.txt` ressemble à ça:
  400. ```
  401. CDG Paris-Charles de Gaulle
  402. ORY Paris-Orly
  403. NCE Nice-Côte d'Azur
  404. ...
  405. ```
  406. Téléchargez-le ici:
  407. \url{https://raw.githubusercontent.com/E2L/cours-python/master/sources/airports.txt}
  408. Clique droit / Enregistrer sous / airports.txt
  409. # fabrique_dico() - 2
  410. ```python
  411. def fabrique_dico():
  412. dico = dict()
  413. fichier = open("airports.txt", "r")
  414. contenu = fichier.read()
  415. fichier.close()
  416. lignes = contenu.splitlines()
  417. for ligne in lignes:
  418. code = ligne[0:3]
  419. nom = ligne[4:]
  420. dico[code] = nom
  421. return dico
  422. ```
  423. # trouve_code()
  424. ```python
  425. def trouve_code(code, dico):
  426. if code in dico:
  427. return dico[code]
  428. ```
  429. \vfill
  430. Notez le `return None` implicite!
  431. # affiche_erreur()
  432. ```python
  433. def affiche_erreur(code):
  434. print(
  435. "Code:", code,
  436. "non trouvé",
  437. file=sys.stderr
  438. )
  439. sys.exit(2)
  440. ```
  441. Notes:
  442. * on affiche le code qu'on a pas trouvé (c'est utile de l'avoir dans
  443. le message)
  444. * valeur d'erreur différente du cas où il n'y avait pas assez
  445. d'arguments
  446. # Rappel du `main()`
  447. ```
  448. def main():
  449. dico = fabrique_dico()
  450. code = lire_code()
  451. nom = trouve_code(code, dico)
  452. if nom:
  453. print(nom)
  454. else:
  455. affiche_erreur(code)
  456. main()
  457. ```
  458. #
  459. \centering \huge Démo
  460. # Place au débat
  461. Quelle approche avez-vous préférée entre bottom-up et top-down?
  462. Pourquoi?