Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
Dieses Repo ist archiviert. Du kannst Dateien sehen und es klonen, kannst aber nicht pushen oder Issues/Pull-Requests öffnen.

python-03.md 11 KiB

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