From e5eaae92cb2304d2413ff997bc36a7604c9f47c4 Mon Sep 17 00:00:00 2001 From: Dimitri Merejkowsky Date: Sat, 29 Dec 2018 16:34:13 +0200 Subject: [PATCH] Rewrite chapter 3 Move unused concepts into a `fragments` subdir Add stuff about sys.stdout, stdin, stderr, error codes bottom-up vs top-down airport example --- sessions/fragments/format.md | 73 ++++ sessions/fragments/sort.md | 92 +++++ sessions/fragments/tuples.md | 208 +++++++++++ sessions/python-03.md | 697 ++++++++++++++++++++--------------- 4 files changed, 779 insertions(+), 291 deletions(-) create mode 100644 sessions/fragments/format.md create mode 100644 sessions/fragments/sort.md create mode 100644 sessions/fragments/tuples.md diff --git a/sessions/fragments/format.md b/sessions/fragments/format.md new file mode 100644 index 0000000..e7cd7cf --- /dev/null +++ b/sessions/fragments/format.md @@ -0,0 +1,73 @@ +% notions: string.format(), mini-language de specification de format + +# Formater des chaînes de caractères + +Problème: + +\vfill + +```python +>>> nom = "Ford" +>>> résultat = 42 +>>> message = "Bonjour, " + nom + ". " +>>> message += "La réponse est: " + str(résultat) + "." +>>> message +'Bonjour, Ford. La réponse est: 42.' +``` + +\vfill + +Ce n'est pas très lisible ... + +# format() + +Solution: utiliser un "template" et la méthode `format()` + +\vfill + +```python +>>> nom = "Ford" +>>> résultat = 42 +>>> template = "Bonjour, {}. La réponse est: {}" +>>> message = template.format(nom, résultat) +>>> message +'Bonjour, Ford. La réponse est: 42.' +``` + +# format() avancé + +On peut aussi nommer les remplacements: + +```python +template = "Bonjour, {nom}. La réponse est: {résultat}" +template.format(nom="Ford", résultat=42) +``` + +# format() avancé + +On peut aussi faire des alignements et du "padding": + +\vfill + +```python +template = "{name:>10}: {score:03}" +print(template.format(name="Alice", score=42)) +print(template.format(name="Bob", score=5)) +``` + +``` + Alice: 042 + Bob: 005 +``` + +# Explications + +Le texte dans les accolades après le `:` est un mini-langage de spécification de format: + +* `>10` signifie: "aligner a droite, taille maximale 10" +* `03` signifie: "rajouter des zéros en début de nombre jusquà atteindre 3 chiffres". + +Plus de précisions dans la documentation: + + +\url{https://docs.python.org/fr/3/library/string.html#format-specification-mini-language}. diff --git a/sessions/fragments/sort.md b/sessions/fragments/sort.md new file mode 100644 index 0000000..63c0c8d --- /dev/null +++ b/sessions/fragments/sort.md @@ -0,0 +1,92 @@ +% notions: sort, lambdas, sort avec une fonction key() + +# Trier des listes + +# sort() - ordre naturel + +```python +>>> nombres = [2, 3, 1, 5] +>>> nombres.sort() +>>> nombres +[1, 2, 3, 5] +``` + +Notez que la liste est modifiée *sur place*. + +# sort() - ordre alphabétique + +```python +>>> mots = ["abeille", "faucon", "chat"] +>>> mots.sort() +>>> mots +['abeille', 'chat', 'faucon'] +``` + +# sort() - ordre lexicographique + +Pour chaque "liste-élément" on compare le premier élément. +S'il y a égalité, on regarde le deuxième élément, etc: + +```python +>>> composite = [["chat", 1], ["abeille", 2], ["chat", 3]] +>>> composite.sort() +>>> composite +[['abeille', 2], ['chat', 1], ['chat', 3]] +``` + +L'ordre alphabétique est l'ordre lexicographique pour les chaînes de caractères :) + +# Attention! + +Tous les éléments de la liste doivent être comparables deux à deux: + +\vfill + +```python +>>> mauvaise_liste = ["un", 2] +>>> mauvaise_liste.sort() +TypeError +``` + + +# Comparer autrement + +Exemple: trier les mots par leur taille avec l'argument `key` + +\vfill + +```python +def taille(mot): + return len(mot) + +mots = ["chat", "abeille", "faucon"] +mots.sort(key=taille) +>>> mots +["chat", "faucon", "abeille"] +``` + +# Lambda + +Sert définir une fonction sans utiliser `def` + +```python +>>> retourne_42 = lambda: 42 # pas d'argument +>>> retourne_42() +42 +>>> ajoute_deux = lambda x: x + 2 # un seul argument +>>> ajoute_deux(3) +5 +>>> multiplie = lambda x, y: x* y # deux arguments +>>> multiplie(2, 3) +6 +``` +Note: le corps de la fonction doit tenir en une seule ligne + +# Utilisation avec sort + +```python +>>> mots = ["chat", "abeille", "faucon"] +>>> mots.sort(key=lambda x: len(x)) +>>> mots +["chat", "faucon", "abeille"] +``` diff --git a/sessions/fragments/tuples.md b/sessions/fragments/tuples.md new file mode 100644 index 0000000..db8d395 --- /dev/null +++ b/sessions/fragments/tuples.md @@ -0,0 +1,208 @@ +% notions: tuples, immuables & mutables, passage par référence, +% copie, retour multiples + +# + +\center Les tuples + + +# Création de tuples + + +```python +mon_tuple = tuple() # un tuple vide +mon_tuple = () # aussi un tuple vide +mon_tuple = (1, 2) # un tuple à deux éléments +``` + +# Note + +C'est la virgule qui fait le tuple, pas les parenthèses +(on n'utilise les parenthèse que pour l'esthétique) + +\vfill + +```python +(1) +# pas un tuple, juste le nombre 1 (entre parenthèses) +(1,) +# un tuple à un élément +1, +# le *même* tuple +``` + +# Indexation, test d'appartenance + +```python +>>> couple = ('Starsky', 'Hutch') +>>> couple[0] +'Starsky' +>>> couple[1] +'Hutch' +>>> couple[3] +IndexError + + +>>> 'Starsky' in couple +True +>>> 'Batman' in couple +False +``` + +Rien de nouveau en principe :p + +# Déstructuration + +Créer plusieurs variables en une seule ligne + +```python +>>> couple = ("Batman", "Robin") +>>> héro, side_kick = couple +>>> héro +'Batman' +>>> side_kick +'Robin' +``` + +On dit aussi: unpacking + +# Quelques erreurs + +```python +>>> héro, side_kick, ennemi = couple +ValueError (3 != 2) + +>>> (héro,) = couple +ValueError (1 != 2) + +# Gare à la virgule: +>>> héro, = couple +ValueError (1 != 2) +``` + +# Pièges + +```python +f(a, b, c) # appelle f() avec trois arguments + +f((a, b, c)) # appelle f() avec un seul argument + # (qui est lui-même un tuple à 3 valeurs) +``` + +# On peut aussi déstructurer des listes + +```python +>>> fruits = ["pomme", "banane", "orange"] +>>> premier, deuxième, troisième = fruits +``` + +# Retour multiple + +Retourner plusieurs valeurs: + +```python +def tire_carte((): + valeur = "10" + couleur = "trèfle" + return (valeur, couleur) + +(v, c) = tire_carte() +print("{} de {}", v, c) +# 10 de trèfle +``` + +\vfill + +En fait c'est juste une manipulation de tuples :) + +# Les tuples sont immuables + +```python +>>> couple = ('Starsky', 'Hutch') +>>> couple[0] = 'Batman' +TypeError +``` + +Les méthodes `count()` et `index()` existent +parce qu'elles ne modifient pas le tuple. + +# Les tuples sont immuables (2) + +Les méthodes qui modifieraient le tuple n'existent pas: + +```python +>>> couple = ('Starsky', 'Hutch') +>>> couple.clear() +AttributeError +``` + + + +# + +\center Mutables, immuables, et références + +# Mutables et immuables + +* Mutables: listes, ensembles, dictionnaires +* Immuables: entiers, flottants, booléens, strings + +# Passage par référence + +En Python, on ne manipule jamais les objets directement, on +ne manipule que des *références* sur ces objets. + +```python +x = 3 # x est une référence vers l'objet 'entier 3' +x = "hello" # x référence une string +``` + +# Exemple 1 + +```python +def ajoute_trois(x): + x = x + 3 # crée une nouvelle référence + +mon_entier = 42 +ajoute_trois(mon_entier) +# 'mon_entier' vaut toujours 42 +``` + +# Exemple 2 + +```python +def ajoute_trois(l): + l.append(3) + # Ne crée pas de nouvelle référence + # Appelle une méthode qui modifie 'l' sur place + +ma_liste = [1, 2] +ajoute_trois(ma_liste) +# 'ma_liste' vaut maintenant [1, 2, 3] +``` + +# Exemple 3 + +```python +def get_max(liste): + autre_liste = liste.copy() + autre_liste.sort() + return autre_liste[-1] + +ma_liste = [1, 3, 2] +x = get_max(ma_liste) +# x = 3 +# `ma_liste` n'a pas changé +``` + +# Notes + +* on peut aussi utiliser la fonction native "max()" +* on peut aussi utiliser une slice: `autre_liste = liste[:]` + +# Conclusion + +Une fonction ne peut modifier la valeur de ses arguments qui s'ils sont +mutables. + +Toutes les copies doivent être explicites diff --git a/sessions/python-03.md b/sessions/python-03.md index 233e08b..7d1c891 100644 --- a/sessions/python-03.md +++ b/sessions/python-03.md @@ -1,184 +1,225 @@ % Programmation avec Python (chapitre 3) % Dimitri Merejkowsky -\centering -Retours sur le chapitre 2 -# - -\centering -Retour sur les listes +\center \huge None -# extend() +# Exprimer l'absence -Pour concaténer des listes: +Exemple: clé non présente dans un dictionnaire: ```python ->>> fruits = ["pomme", "banane"] ->>> fruits.extend(["orange", "abricot"]) ->>> fruits -['pomme', 'banane', 'orange', 'abricot'] +>>> scores = { "Anne": 42, "Bernard": 5 } +>>> score1 = scores.get("Anne") +>>> score1 +42 +>>> score2 = scores.get("Sophie") +>>> score2 + ``` -\vfill -Peut aussi s'écrire `+=` +En réalité, `score2` a bien une valeur: `None`. -```python ->>> nombres = [1, 2] ->>> nombres += [3, 4] ->>> nombres -[1, 2, 3, 4] -``` +L'interpréteur n'affiche rien quand la valeur est `None` -# pop() -`pop()` existe aussi pour les listes: +# None est ambigu -\vfill +None est `falsy`, tout comme 0, False et les listes vides: ```python ->>> fruits = ["pomme", "banane"] ->>> fruits.pop() # Retourne l'élément -'pomme' ->>> fruits # Et modifie la liste -["banane"] - ->>> vide = list() ->>> vide.pop() -IndexError +element = dictionnaire.get(clé) +if not element: + ... ``` -# clear() +Mais ici, comment faire la différence entre: -Pour vider une liste: +* 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 ->>> fruits = ["pomme", "banane"] ->>> fruits.clear() ->>> fruits -[] +if clé in dictionnaire: + # La clé existe, pas d'erreur + valeur = dictionnaire[clé] ``` -# index() +# Solution 2: tester None -Pour récupérer la position d'un élément: +Avec `is`: ```python ->>> fruits = ["pomme", "banane"] ->>> fruits.index("banane") ->>> 1 ->>> fruits.index("citron") ->> ValueError +>>> scores = { "Anne": 42, "Bernard": 5 } +>>> score1 = scores.get("Anne") +>>> score1 is None +False +>>> score2 = scores.get("Sophie") +>>> score2 is None +True ``` -# count() -Pour compter le nombre d'occurrences d'un élément +# 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 ->>> fruits = ["pomme", "banane", "pomme", "poire"] ->>> fruits.count("pomme") -2 ->>> fruits.count("poire") -1 ->>> fruits.count("citron") -0 +>>> 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. -# sort() +# Retourner None au autre chose + +```python +def trouve_dans_liste1(liste, element): + if element in list: + return liste.index(element) -Pour trier une liste. + +def trouve_dans_liste2(liste, element): + if element in list: + return liste.index(element) + else: + return None +``` \vfill -* Par ordre naturel +Les deux fonctions font la même chose : `trouve_dans_liste2` est simplement plus *explicite.* + +# Types optionnels ```python ->>> nombres = [2, 3, 1, 5] ->>> nombres.sort() ->>> nombres -[1, 2, 3, 5] +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*. -\vfill +# + +\centering \huge Retour sur les listes -* Par ordre alphabétique +# Méthodes ```python ->>> mots = ["abeille", "faucon", "chat"] ->>> mots.sort() -['abeille', 'chat', 'faucon'] +fruits = ["pomme", "banane"] +fruits.append("orange") ``` -# Ordre lexicographique +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. -Pour chaque "liste-élément" on compare le premier élément. -S'il y a égalité, on regarde le deuxième élément, etc: +On appelle ce genre de fonction une **méthode**. + +# clear() + +Pour vider une liste: ```python ->>> composite = [["chat", 1], ["abeille", 2], ["chat", 3]] ->>> composite.sort() -[['abeille', 2], ['chat', 1], ['chat', 3]] +>>> fruits = ["pomme", "banane"] +>>> fruits.clear() +>>> fruits +[] ``` -\vfill +Notez que la méthode `clear()` ne renvoie rien! +La liste est modifiée *sur place*. -L'ordre alphabétique est l'ordre lexicographique pour les chaînes de caractères :) -# Attention! +# extend() + +Pour concaténer des listes: ```python ->>> mauvaise_liste = ["un", 2] ->>> mauvaise_liste.sort() -TypeError +>>> 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] +``` -# Comparer autrement -Trier les mots par leur taille +# pop() -* Avec l'argument `key` +On a vu la méthode `pop()` pour les dictionnaires, mais elle existe +aussi pour les listes: \vfill ```python -def taille(mot): - return len(mot) +>>> fruits = ["pomme", "banane"] +>>> fruits.pop() # Retourne l'élément +'pomme' +>>> fruits # Et modifie la liste +["banane"] +``` -mots = ["chat", "abeille", "faucon"] -mots.sort(key=taille) ->>> mots -["chat", "faucon", "abeille"] +# pop() sur liste vide + +```python +>>> liste_vide = list() +>>> liste_vide.pop() +IndexError ``` -# Lambda -Sert définir une fonction sans utiliser `def` +# index() + +Pour récupérer la position d'un élément: ```python ->>> retourne_42 = lambda: 42 # pas d'argument ->>> retourne_42() -42 ->>> ajoute_deux = lambda x: x + 2 # un seul argument ->>> ajoute_deux(3) -5 ->>> multiplie = lambda x, y: x* y # deux arguments ->>> multiplie(2, 3) -6 +>>> fruits = ["pomme", "banane"] +>>> fruits.index("banane") +>>> 1 +>>> fruits.index("citron") +>> ValueError ``` -Note: le corps de la fonction doit tenir en une seule ligne -# Utilisation avec sort +# count() + +Pour compter le nombre d'occurrences d'un élément ```python ->>> mots = ["chat", "abeille", "faucon"] ->>> mots.sort(key=lambda x: len(x)) ->>> mots -["chat", "faucon", "abeille"] +>>> fruits = ["pomme", "banane", "pomme", "poire"] +>>> fruits.count("pomme") +2 +>>> fruits.count("poire") +1 +>>> fruits.count("citron") +0 ``` + # Indexer des listes Rappel: @@ -214,14 +255,19 @@ Ou "faire des slices", ou "slicer". ['d', 'e'] >>> lettres[1:-2] # fin négative ['b', 'c'] ->>> lettres[:] # une copie -['a', 'b', 'c', 'd', 'e'] ``` + # -\centering -Retour sur les strings +\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 @@ -235,7 +281,7 @@ Retour sur les strings # Trancher des chaînes de caractères -Ou slicer des strings: +On peu aussi slicer des strings: \vfill @@ -249,290 +295,359 @@ Ou slicer des strings: 'monde' ``` +# Les strings sont immuables -# Formater des chaînes de caractères +Mais on ne **peut pas** modifier une string "sur place". -Problème: - -\vfill +```python +>>> message = "Bonjour, monde !" +>>> message[-1] = "?" +TypeError +``` ```python ->>> nom = "Ford" ->>> résultat = 42 ->>> message = "Bonjour, " + nom + ". " ->>> message += "La réponse est: " + str(résultat) + "." ->>> message -'Bonjour, Ford. La réponse est: 42.' +>>> message = "Bonjour, monde !" +>>> message.clear() +AttributeError ``` -\vfill +# -Ce n'est pas très lisible! +\centering \huge La ligne de commande -# format() +# Schéma -Solution: utiliser un "template" et la méthode `format()` +![programme](programme.png) -\vfill +# Entrée / sortie -```python ->>> nom = "Ford" ->>> résultat = 42 ->>> template = "Bonjour, {}. La réponse est: {}" ->>> message = template.format(nom, résultat) ->>> message -'Bonjour, Ford. La réponse est: 42.' -``` +3 canaux: -# format() avancé +* Une entrée qu'on peut lire (`stdin`) +* Deux sorties: + * Sortie normale (ou standard) (`stdout`) + * Sortie d'erreur (`stdout`) -On peut aussi nommer les remplacements: -```python -template = "Bonjour, {nom}. La réponse est: {résultat}" -template.format(nom="Ford", résultat=42) -``` +# Accès en Python -# format() avancé +* Pour lire stdin: `input()` +* Pour écrire dans stdout: `print()` +* Pour écrire dans stderr: pas de fonction native -On peut aussi faire des alignements et du "padding": +# Accès en Python (2) -\vfill +Rajouter `import sys` en haut du fichier, puis: -```python -template = "{name:>10}: {score:03}" -print(template.format(name="Alice", score=42)) -print(template.format(name="Bob", score=5)) -``` +* `sys.stdin.read()` +* `sys.stout.write()` +* `sys.stderr.write()` -``` - Alice: 042 - Bob: 005 -``` +On peut aussi utiliser: -# Explications +`print("erreur", file=sys.stderr)` -Le texte dans les accolades après le `:` est un mini-langage de spécification de format: -* `>10` signifie: "aligner a droite, taille maximale 10" -* `03` signifie: "rajouter des zéros en début de nombre jusquà atteindre 3 chiffres". +# Accès depuis l'invite de commande -Plus de précisions dans la documentation: +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 -\url{https://docs.python.org/fr/3/library/string.html#format-specification-mini-language}. +# Rediriger stdout -# +Linux , macOS, Windows: -\center -None +``` +python3 fichier.py > output.txt +``` -# Exprimer l'absence +stdout sera écrit dans `output.txt`, et seul `stderr` sera visible. -```python ->>> scores = { "Anne": 42, "Bernard": 5 } ->>> score1 = scores.get("Anne") ->>> score1 -42 ->>> score2 = scores.get("Sophie") ->>> score2 - -``` -En réalité, `score2` a bien une valeur: `None`. +# Code de retour -L'interpréteur n'affiche rien quand la valeur est `None` +* 0 quand tout va bien +* un autre entier quand quelque chose va mal -# None est falsy +\vfill -```python -element = dictionnaire.get(clé) -if element: - ... -``` +> Toutes les familles heureuses se ressemblent, mais chaque famille +> malheureuse l'est à sa façon. +> +> Tolstoï -Mais ici, comment faire la différence entre: +# Code de retour -* la clé existe et vaut 0, une chaîne vide, ou quoique ce soit de falsy -* la clé existe et vaut None -* la clé n'existe pas +Les valeurs d'erreur possibles sont en général présentes +dans la documentation. -# Tester l'appartenance +Note: **ne pas retourner 0** en cas d'erreur, même minime, et même si +un message a été affiché. -Avec `in`: +C'est important pour pourvoir composer plusieurs programmes (on y +reviendra). -```python -if clé in dictionnaire: - # La clé existe, pas d'erreur - valeur = dictionnaire[clé] + +# Afficher le code retour depuis le shell + +```bash +# Linux, macOS +$ python3 code.py +$ echo $? +0 ``` -# Tester None +```bash +# Windows +> python3 code.py +> echo %ERRORLEVEL% +0 +``` -Avec `is`: +# 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 ->>> scores = { "Anne": 42, "Bernard": 5 } ->>> score1 = scores.get("Anne") ->>> score1 is None -False ->>> score2 = scores.get("Sophie") ->>> score2 is None -True +import sys + +def fait_quelque_chose(): + if erreur: + sys.exit(1) ``` -# Lever l'ambiguïté +Note: dans un vrai programme, veillez à construire et afficher un +message utile! -Notez qu'ici Sophie peut être dans le dictionnaire, mais avec une valeur 'None', -ou bien ne pas y être. +# Gestion du code de retour en Python (2) -Attention aux ambiguïtés, donc! +`sys.exit(0)` pour terminer immédiatement et sans erreur. -Pas de méthode magique : il faut être au courant du problème. +```python +import sys +def antivirus(): + problèmes = cherche_problèmes() -# Retourner None + if not problèmes: + print("Aucun problème trouvé") + sys.exit(0) -`None` est aussi la valeur par défaut lorsqu'il n'y a pas de `return` -dans le corps de la fonction: + 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 ->>> def ne_retourne_rien(a, b): ->>> c = a + b +if erreur: + sys.exit("Une erreur est survenue") ->>> résultat = ne_retourne_rien(3, 2) ->>> résultat is None -True ``` -# Retourner None au autre chose +# Les arguments d'un programme -```python -def trouve_dans_liste1(liste, element): - if element in list: - return liste.index(element) +Pour lancer un programme, on tape son nom, suivi de mots séparés pas +des espaces. +En Python, ça donne -def trouve_dans_liste2(liste, element): - if element in list: - return liste.index(element) - else: - return None +``` +python3 fichier.py arg1 arg2 ``` -\vfill +# Accès aux arguments en Python -Les deux fonctions font la même chose! `trouve_dans_liste2` est simplement plus *explicite.* +* `import sys` +* `sys.argv` -# Types optionnels +`sys.argv` est une liste, et son premier argument est +*toujours* le nom du fichier exécuté. + +# Exemple ```python -def trouve_dans_liste(liste, element): - if element in list: - return liste.index(element) - else: - return None +# 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'] ``` -On dit aussi que le type de retour de `trouve_dans_liste` est *optionnel*. # -\center Les tuples +\center \huge Cas pratique -# Création de tuples +# Squelette +Décodeur de noms d'aéroports -```python -mon_tuple = tuple() # un tuple vide -mon_tuple = () # aussi un tuple vide -mon_tuple = (1, 2) # un tuple à deux éléments -``` +* 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. -# Note +# Différentes approches -C'est la virgule qui fait le tuple, pas les parenthèses -(on n'utilise les parenthèse que pour l'esthétique) +* Bottom-up (approche *ascendante*) +* Top-Bottom (approche *descendante*) -\vfill +# 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 -(1) -# pas un tuple, juste le nombre 1 (entre parenthèses) -(1,) -# un tuple à un élément -1, -# le *même* tuple +def main(): + dico = fabrique_dico() + code = lire_code() + nom = trouve_code(code, dico) + if nom: + print(nom) + else: + affiche_erreur(code) + +main() ``` -# Indexation, test d'appartenance +On a fait comme si le code dont on avait besoin était déjà écrit :) + +# lire_code() ```python ->>> couple = ('Starsky', 'Hutch') ->>> couple[0] -'Starsky' ->>> couple[1] -'Hutch' ->>> couple[3] -IndexError +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: ->>> 'Starsky' in couple -True ->>> 'Batman' in couple -False +``` +CDG Paris-Charles de Gaulle +ORY Paris-Orly +NCE Nice-Côte d'Azur +... ``` -Rien de nouveau en principe :p +Téléchargez-le ici: -# Déstructuration +\url{https://raw.githubusercontent.com/E2L/cours-python/master/sources/airports.txt} -Créer plusieurs variables en une seule ligne +Clique droit / Enregistrer sous / airports.txt -```python ->>> couple = ("Batman", "Robin") ->>> héro, side_kick = couple ->>> héro -'Batman' ->>> side_kick -'Robin' +# fabrique_dico() - 2 ->>> héro, side_kick, ennemi = couple -ValueError 3 != 2 ->>> héro, = couple -ValueError 1 != 2 -ValueError +```python +def fabrique_dico(): + dico = dict() + fichier = open("airports.txt", "r") + contenu = fichier.read() + fichier.close() ->>> héro = couple -# OK, mais la variable héro est maintenant un tuple ... + lignes = contenu.splitlines() + for ligne in lignes: + code = ligne[0:3] + nom = ligne[4:] + dico[code] = nom + return dico ``` -# On peut aussi déstructurer des listes +# trouve_code() ```python ->>> fruits = ["pomme", "banane", "orange"] ->>> premier, deuxième, troisième = fruits +def trouve_code(code, dico): + if code in dico: + return dico[code] ``` -# Retours multiple +\vfill -Retourner plusieurs valeurs: +Notez le `return None` implicite! -#TODO: better Exemple, pleaz -```python -def age_sexe_ville(pseudo): - age = ... - sexe = .. - ville = ... - return (age, sexe, ville) +# affiche_erreur() -(a, s, v) = age_sexe_ville('kevin') -# on dit aussi: unpacking +```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()` + + ``` -a -> 14 -s -> M -v -> Paris +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 + + +# Pour la prochaine fois + +Prenez le temps de réfléchir :) + +Quelle approche avez-vous préféré ? Pourquoi ?