% Programmation avec Python (chapitre 3) % Dimitri Merejkowsky
\center \huge None
>>> scores = { "Anne": 42, "Bernard": 5 }
>>> scores["Anne"]
42
>>> scores.get("Anne")
42
>>> scores["Sophie"]
KeyError
>>> scores.get("Sophie")
<rien>Que se passe-t-il?
En réalité, get() retourne None quand la clé n'est pas présente
\vfill
>>> a = 42
>>> a
42
>>> b = None
>>> b
<rien>None est falsy, tout comme 0, False et les listes vides:
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 ?
Avec in:
if clé in dictionnaire:
# La clé existe, pas d'erreur
valeur = dictionnaire[clé]Avec is:
>>> scores = { "Anne": 42, "Bernard": 5 }
>>> score1 = scores.get("Anne")
>>> score1 is None
False
>>> score2 = scores.get("Sophie")
>>> score2 is None
TrueNone est aussi la valeur par défaut lorsqu'il n'y a pas de return
dans le corps de la fonction:
>>> def ne_retourne_rien(a, b):
>>> c = a + b
>>> résultat = ne_retourne_rien(3, 2)
>>> résultat is None
TrueOn 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.
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.
def trouve_dans_liste(liste, element):
if element in list:
return liste.index(element)
else:
return NoneOn dit aussi que le type de retour de trouve_dans_liste est optionnel.
\centering \huge Retour sur les listes
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.
Pour vider une liste:
>>> fruits = ["pomme", "banane"]
>>> fruits.clear()
>>> fruits
[]Notez que la méthode clear() ne renvoie rien!
La liste est modifiée sur place.
Pour concaténer des listes:
>>> fruits = ["pomme", "banane"]
>>> fruits.extend(["orange", "abricot"])
>>> fruits
['pomme', 'banane', 'orange', 'abricot']\vfill
Peut aussi s'écrire +=
>>> nombres = [1, 2]
>>> nombres += [3, 4]
>>> nombres
[1, 2, 3, 4]On a vu la méthode pop() pour les dictionnaires, mais elle existe
aussi pour les listes:
\vfill
>>> fruits = ["pomme", "banane"]
>>> fruits.pop() # Retourne l'élément
'pomme'
>>> fruits # Et modifie la liste
["banane"]>>> liste_vide = list()
>>> liste_vide.pop()
IndexErrorPour récupérer la position d'un élément:
>>> fruits = ["pomme", "banane"]
>>> fruits.index("banane")
>>> 1
>>> fruits.index("citron")
>> ValueErrorPour compter le nombre d'occurrences d'un élément
>>> fruits = ["pomme", "banane", "pomme", "poire"]
>>> fruits.count("pomme")
2
>>> fruits.count("poire")
1
>>> fruits.count("citron")
0Rappel:
>>> lettres = ["a", "b", "c", "d", "e"]
>>> lettres[0] # ça commence à zéro
"a"
>>> lettres[4]
"e"Mais on peut aussi compter à l'envers:
>>> lettres[-1]
"e"
>>> lettres[-2]
"d"Ou "faire des slices", ou "slicer".
>>> 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
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.
>>> message = "Bonjour, monde !"
>>> message.index("B")
0
>>> message.count("o")
3On peu aussi slicer des strings:
\vfill
>>> message = "Bonjour, monde !"
>>> message[1:4]
'onj'
>>> message[:7]
'Bonjour'
>>> message[9:-2]
'monde'Mais on ne peut pas modifier une string "sur place".
>>> message = "Bonjour, monde !"
>>> message[-1] = "?"
TypeError>>> message = "Bonjour, monde !"
>>> message.clear()
AttributeError\centering \huge La ligne de commande
3 canaux:
- Une entrée qu'on peut lire (
stdin) - Deux sorties:
- Sortie normale (ou standard) (
stdout) - Sortie d'erreur (
stdout)
- Sortie normale (ou standard) (
- Pour lire stdin:
input() - Pour écrire dans stdout:
print() - Pour écrire dans stderr: pas de fonction native
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)
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
Linux , macOS, Windows:
python3 fichier.py > output.txt
stdout sera écrit dans output.txt, et seul stderr sera visible.
- 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ï
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).
# Linux, macOS
$ python3 code.py
$ echo $?
0# Windows
> python3 code.py
> echo %ERRORLEVEL%
0Par défaut, le code de retour est 0.
On peut terminer le programme avec sys.exit() et un numéro:
import sys
def fait_quelque_chose():
if erreur:
sys.exit(1)Note: dans un vrai programme, veillez à construire et afficher un message utile!
sys.exit(0) pour terminer immédiatement et sans erreur.
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
...On peut passer le message d'erreur directement à sys.exit() avec
une string au lieu d'un numéro:
if erreur:
sys.exit("Une erreur est survenue")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
import syssys.argv
sys.argv est une liste, et son premier argument est
toujours le nom du fichier exécuté.
# 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
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.
- Bottom-up (approche ascendante)
- Top-Down (approche descendante)
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()
Essayons de partir du plus "haut niveau" et de "descendre" vers les blocs élémentaires
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 :)
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 codeLe 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
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 dicodef trouve_code(code, dico):
if code in dico:
return dico[code]\vfill
Notez le return None implicite!
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
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
Quelle approche avez-vous préférée entre bottom-up et top-down?
Pourquoi?
