% Programmation avec Python (chapitre 3) % Dimitri Merejkowsky
\center \huge None
Exemple: clé non présente dans un dictionnaire:
>>> scores = { "Anne": 42, "Bernard": 5 }
>>> score1 = scores.get("Anne")
>>> score1
42
>>> score2 = scores.get("Sophie")
>>> score2
<rien>
En réalité, score2
a bien une valeur: None
.
L’interpréteur n’affiche rien quand la valeur est None
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:
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
True
None
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
True
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.
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 None
On 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()
IndexError
Pour récupérer la position d’un élément:
>>> fruits = ["pomme", "banane"]
>>> fruits.index("banane")
>>> 1
>>> fruits.index("citron")
>> ValueError
Pour 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")
0
Rappel:
>>> 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")
3
On 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:
stdin
)stdout
)stdout
)input()
print()
Rajouter import sys
en haut du fichier, puis:
sys.stdin.read()
sys.stout.write()
sys.stderr.write()
On peut aussi utiliser:
print("erreur", file=sys.stderr)
On dit aussi shell, ou CLI (pour command line interface).
Linux , macOS, Windows:
python3 fichier.py > output.txt
stdout sera écrit dans output.txt
, et seul stderr
sera visible.
\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%
0
Par 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 sys
sys.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
Utilisé pour le pendu:
has_won
,
display_hint
, etc...)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 code
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
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
def 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:
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
Quelle approche avez-vous préférée entre bottom-up et top-down?
Pourquoi?