|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667 |
- % Programmation avec Python (chapitre 3)
- % Dimitri Merejkowsky
-
-
- \center \huge None
-
- # Jouons avec les dictionnaires
-
- ```python
- >>> scores = { "Anne": 42, "Bernard": 5 }
- >>> scores["Anne"]
- 42
- >>> scores.get("Anne")
- 42
-
- >>> scores["Sophie"]
- KeyError
- >>> scores.get("Sophie")
- <rien>
- ```
-
- Que se passe-t-il?
-
- # Exprimer l'absence
-
- En réalité, `get()` retourne None quand la clé n'est pas présente
-
- \vfill
-
- ```python
- >>> a = 42
- >>> a
- 42
- >>> b = None
- >>> b
- <rien>
- ```
-
-
- # None est ambigu
-
- None est `falsy`, tout comme 0, False et les listes vides:
-
- ```python
- element = dictionnaire.get(clé)
- if not element:
- ...
- ```
-
- Mais ici, comment faire la différence entre:
-
- * la clé existe et vaut None
- * la clé existe et est falsy
- * la clé n'existe pas ?
-
- # Solution 1: tester l'appartenance
-
- Avec `in`:
-
- ```python
- if clé in dictionnaire:
- # La clé existe, pas d'erreur
- valeur = dictionnaire[clé]
- ```
-
- # Solution 2: tester None
-
- Avec `is`:
-
- ```python
- >>> scores = { "Anne": 42, "Bernard": 5 }
- >>> score1 = scores.get("Anne")
- >>> score1 is None
- False
- >>> score2 = scores.get("Sophie")
- >>> score2 is None
- True
- ```
-
-
- # Retourner None
-
- `None` est aussi la valeur par défaut lorsqu'il n'y a pas de `return`
- dans le corps de la fonction:
-
- ```python
- >>> def ne_retourne_rien(a, b):
- >>> c = a + b
-
- >>> résultat = ne_retourne_rien(3, 2)
- >>> résultat is None
- True
- ```
-
- # Retourner None (2)
-
- On dit aussi que la fonction est une *procédure*.
-
- On a déjà écrit des procédures: dans `pendu.py`, `display_hint()` et `main()`
- ne retournaient rien.
-
- # Retourner None au autre chose
-
- ```python
- def trouve_dans_liste1(liste, element):
- if element in list:
- return liste.index(element)
-
-
- def trouve_dans_liste2(liste, element):
- if element in list:
- return liste.index(element)
- else:
- return None
- ```
-
- \vfill
-
- Les deux fonctions font la même chose : `trouve_dans_liste2` est simplement plus *explicite.*
-
- # Types optionnels
-
- ```python
- def trouve_dans_liste(liste, element):
- if element in list:
- return liste.index(element)
- else:
- return None
- ```
- On dit aussi que le type de retour de `trouve_dans_liste` est *optionnel*.
-
- #
-
- \centering \huge Retour sur les listes
-
- # Méthodes
-
- ```python
- fruits = ["pomme", "banane"]
- fruits.append("orange")
- ```
-
- Quand on écrit `fruits.append("orange")`, on peut voir `append()`
- comme une "fonction" qui prendrait `fruits` en argument implicite
- (avant le `.`), et `orange` en argument explicite.
-
- On appelle ce genre de fonction une **méthode**.
-
- # clear()
-
- Pour vider une liste:
-
- ```python
- >>> fruits = ["pomme", "banane"]
- >>> fruits.clear()
- >>> fruits
- []
- ```
-
- Notez que la méthode `clear()` ne renvoie rien!
- La liste est modifiée *sur place*.
-
-
- # extend()
-
- Pour concaténer des listes:
-
- ```python
- >>> fruits = ["pomme", "banane"]
- >>> fruits.extend(["orange", "abricot"])
- >>> fruits
- ['pomme', 'banane', 'orange', 'abricot']
- ```
- \vfill
-
- Peut aussi s'écrire `+=`
-
- ```python
- >>> nombres = [1, 2]
- >>> nombres += [3, 4]
- >>> nombres
- [1, 2, 3, 4]
- ```
-
-
- # pop()
-
- On a vu la méthode `pop()` pour les dictionnaires, mais elle existe
- aussi pour les listes:
-
- \vfill
-
- ```python
- >>> fruits = ["pomme", "banane"]
- >>> fruits.pop() # Retourne l'élément
- 'pomme'
- >>> fruits # Et modifie la liste
- ["banane"]
- ```
-
- # pop() sur liste vide
-
- ```python
- >>> liste_vide = list()
- >>> liste_vide.pop()
- IndexError
- ```
-
-
- # index()
-
- Pour récupérer la position d'un élément:
-
- ```python
- >>> fruits = ["pomme", "banane"]
- >>> fruits.index("banane")
- >>> 1
- >>> fruits.index("citron")
- >> ValueError
- ```
-
- # count()
-
- Pour compter le nombre d'occurrences d'un élément
-
- ```python
- >>> fruits = ["pomme", "banane", "pomme", "poire"]
- >>> fruits.count("pomme")
- 2
- >>> fruits.count("poire")
- 1
- >>> fruits.count("citron")
- 0
- ```
-
-
- # Indexer des listes
-
- Rappel:
-
- ```python
- >>> lettres = ["a", "b", "c", "d", "e"]
- >>> lettres[0] # ça commence à zéro
- "a"
- >>> lettres[4]
- "e"
- ```
-
- Mais on peut aussi compter à l'envers:
-
- ```python
- >>> lettres[-1]
- "e"
- >>> lettres[-2]
- "d"
- ```
-
- # Trancher des listes
-
- Ou "faire des slices", ou "slicer".
-
- ```python
- >>> lettres = ["a", "b", "c", "d", "e"]
- >>> lettres[1:3] # début (inclus), fin (non-inclus)
- ['b', 'c']
- >>> lettres[:3] # début implicite
- ['a', 'b', 'c']
- >>> lettres[3:] # fin implicite
- ['d', 'e']
- >>> lettres[1:-2] # fin négative
- ['b', 'c']
- ```
-
-
- #
-
- \centering \huge Retour sur les strings
-
- # Rappel
-
- On a vu que les strings étaient "presque" des liste de caractères.
-
- Par exemple, on peut itérer sur les lettres d'un mot avec: `for lettre
- in mot`.
-
- # index() et count() marchent aussi
-
- ```python
- >>> message = "Bonjour, monde !"
- >>> message.index("B")
- 0
- >>> message.count("o")
- 3
- ```
-
- # Trancher des chaînes de caractères
-
- On peu aussi slicer des strings:
-
- \vfill
-
- ```python
- >>> message = "Bonjour, monde !"
- >>> message[1:4]
- 'onj'
- >>> message[:7]
- 'Bonjour'
- >>> message[9:-2]
- 'monde'
- ```
-
- # Les strings sont immuables
-
- Mais on ne **peut pas** modifier une string "sur place".
-
- ```python
- >>> message = "Bonjour, monde !"
- >>> message[-1] = "?"
- TypeError
- ```
-
- ```python
- >>> message = "Bonjour, monde !"
- >>> message.clear()
- AttributeError
- ```
-
- #
-
- \centering \huge La ligne de commande
-
- # Schéma
-
- ![programme](img/programme.png)
-
- # Entrée / sortie
-
- 3 canaux:
-
- * Une entrée qu'on peut lire (`stdin`)
- * Deux sorties:
- * Sortie normale (ou standard) (`stdout`)
- * Sortie d'erreur (`stdout`)
-
-
- # Accès en Python
-
- * Pour lire stdin: `input()`
- * Pour écrire dans stdout: `print()`
- * Pour écrire dans stderr: pas de fonction native
-
- # Accès en Python (2)
-
- Rajouter `import sys` en haut du fichier, puis:
-
- * `sys.stdin.read()`
- * `sys.stdout.write()`
- * `sys.stderr.write()`
-
- On peut aussi utiliser:
-
- `print("erreur", file=sys.stderr)`
-
-
- # Accès depuis l'invite de commande
-
- On dit aussi *shell*, ou *CLI* (pour *command line interface*).
-
- * stdin: Taper quand le shell vous laisse la main, finir par 'entrée'
- * stdout et stderr sont affichés en même temps pas défaut
-
- # Rediriger stdout
-
- Linux , macOS, Windows:
-
- ```
- python3 fichier.py > output.txt
- ```
-
- stdout sera écrit dans `output.txt`, et seul `stderr` sera visible.
-
-
- # Code de retour
-
-
- * 0 quand tout va bien
- * un autre entier quand quelque chose va mal
-
- \vfill
-
- > Toutes les familles heureuses se ressemblent, mais chaque famille
- > malheureuse l'est à sa façon.
- >
- > Tolstoï
-
- # Code de retour
-
- Les valeurs d'erreur possibles sont en général présentes
- dans la documentation.
-
- Note: **ne pas retourner 0** en cas d'erreur, même minime, et même si
- un message a été affiché.
-
- C'est important pour pourvoir composer plusieurs programmes (on y
- reviendra).
-
-
- # Afficher le code retour depuis le shell
-
- ```bash
- # Linux, macOS
- $ python3 code.py
- $ echo $?
- 0
- ```
-
- ```bash
- # Windows
- > python3 code.py
- > echo %ERRORLEVEL%
- 0
- ```
-
- # Gestion du code de retour en Python
-
- Par défaut, le code de retour est 0.
-
- On peut terminer le programme avec `sys.exit()` et un numéro:
-
- ```python
- import sys
-
- def fait_quelque_chose():
- if erreur:
- sys.exit(1)
- ```
-
- Note: dans un vrai programme, veillez à construire et afficher un
- message utile!
-
- # Gestion du code de retour en Python (2)
-
- `sys.exit(0)` pour terminer immédiatement et sans erreur.
-
- ```python
- import sys
-
- def antivirus():
- problèmes = cherche_problèmes()
-
- if not problèmes:
- print("Aucun problème trouvé")
- sys.exit(0)
-
- for problème in problèmes:
- # traite chaque problème un à un
- ...
- ````
-
- # Gestion du code de retour en Python - un raccourci
-
- On peut passer le message d'erreur directement à `sys.exit()` avec
- une string au lieu d'un numéro:
-
- ```python
- if erreur:
- sys.exit("Une erreur est survenue")
-
- ```
-
- # Les arguments d'un programme
-
- Pour lancer un programme, on tape son nom, suivi de mots séparés pas
- des espaces.
-
- En Python, ça donne
-
- ```
- python3 fichier.py arg1 arg2
- ```
-
- # Accès aux arguments en Python
-
- * `import sys`
- * `sys.argv`
-
- `sys.argv` est une liste, et son premier argument est
- *toujours* le nom du fichier exécuté.
-
- # Exemple
-
- ```python
- # Dans foo.py
- import sys
- print("Fichier source:", sys.argv[0])
- print("Reste des arguments:", sys.argv[1:])
- ```
-
- \vfill
-
- ```
- $ python3 foo.py un deux
- Fichier source: foo.py
- Reste des arguments: ['un', 'deux']
- ```
-
- #
-
- \center \huge Cas pratique
-
- # Squelette
-
- Décodeur de noms d'aéroports
-
- * Lire un fichier avec les codes et les noms associés
- * En faire un dictionnaire
- * Utiliser le premier argument comme nom de code
- * Afficher le nom complet de l'aéeroport, ou une
- erreur s'il est inconnu.
-
- # Différentes approches
-
- * Bottom-up (approche *ascendante*)
- * Top-Down (approche *descendante*)
-
- # Approches bottom-up
-
- Utilisé pour le pendu:
-
- * construire des blocs élémentaires (les petites fonctions `has_won`,
- `display_hint`, etc...)
- * les assembler pour faire un tout plus "haut niveau" (le corps de la
- fonction `main()`
-
- # Approche top-down
-
- Essayons de partir du plus "haut niveau" et de "descendre" vers les
- blocs élémentaires
-
- # Code
-
- ```python
- def main():
- dico = fabrique_dico()
- code = lire_code()
- nom = trouve_code(code, dico)
- if nom:
- print(nom)
- else:
- affiche_erreur(code)
-
- main()
- ```
-
- On a fait comme si le code dont on avait besoin était déjà écrit :)
-
- # lire_code()
-
- ```python
- import sys
-
- def lire_code():
- if len(sys.argv) < 2:
- print(
- "Pas assez d'arguments",
- file=sys.stderr
- )
- sys.exit(1)
- argument = sys.argv[1]
- # On accepte `cdg` ou `CDG`.
- code = argument.upper()
- return code
- ```
-
-
- # fabrique_dico()
-
- Le fichier `airports.txt` ressemble à ça:
-
- ```
- CDG Paris-Charles de Gaulle
- ORY Paris-Orly
- NCE Nice-Côte d'Azur
- ...
- ```
-
- Téléchargez-le ici:
-
- \url{https://raw.githubusercontent.com/E2L/cours-python/master/sources/airports.txt}
-
- Clique droit / Enregistrer sous / airports.txt
-
- # fabrique_dico() - 2
-
- ```python
- def fabrique_dico():
- dico = dict()
- fichier = open("airports.txt", "r")
- contenu = fichier.read()
- fichier.close()
-
- lignes = contenu.splitlines()
- for ligne in lignes:
- code = ligne[0:3]
- nom = ligne[4:]
- dico[code] = nom
- return dico
- ```
-
- # trouve_code()
-
- ```python
- def trouve_code(code, dico):
- if code in dico:
- return dico[code]
- ```
-
- \vfill
-
- Notez le `return None` implicite!
-
-
- # affiche_erreur()
-
- ```python
- def affiche_erreur(code):
- print(
- "Code:", code,
- "non trouvé",
- file=sys.stderr
- )
- sys.exit(2)
- ```
-
- Notes:
-
- * on affiche le code qu'on a pas trouvé (c'est utile de l'avoir dans
- le message)
- * valeur d'erreur différente du cas où il n'y avait pas assez
- d'arguments
-
- # Rappel du `main()`
-
-
- ```
- def main():
- dico = fabrique_dico()
- code = lire_code()
- nom = trouve_code(code, dico)
- if nom:
- print(nom)
- else:
- affiche_erreur(code)
-
- main()
- ```
-
- #
-
- \centering \huge Démo
-
-
- # Place au débat
-
- Quelle approche avez-vous préférée entre bottom-up et top-down?
-
- Pourquoi?
|