TD12. Redonnons la main
Le sujet de ce TD est disponible au format pdf ici.
L'objectif de ce TD est d'introduire les générateurs Python, c'est à dire le mot-clef yield
, et le concept fondamental d'itérateur qui se cache derrière ainsi que le concept d'itérable spécifique à Python.
Exercice 1 : quel est le problème ?
Question 1
Analysez attentivement le code ci-dessous. Combien de lignes du fichier d'entrée faut-il lire pour que le programme affiche la note de l'étudiant situé en toute première position dans le fichier ?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 | #!/usr/bin/env python3
import sys
def traite_fichier(nom_fichier):
fichier = open(nom_fichier, "r")
resultats = []
for ligne in fichier:
ligne_decoupee = ligne.split(" ")
prenom = ligne_decoupee[0]
note = int(ligne_decoupee[1])
resultats.append((prenom, note))
fichier.close()
return resultats
def cherche_note(prenom, prenoms_notes):
for resultat in prenoms_notes:
if resultat[0] == prenom:
return resultat[1]
return None
def get_note():
if len(sys.argv) != 3:
print("Usage :", sys.argv[0], "nom_fichier prenom")
return
prenoms_notes = traite_fichier(sys.argv[1])
prenom = sys.argv[2]
print("La note de", prenom, "est", cherche_note(prenom, prenoms_notes))
if __name__ == "__main__":
get_note()
|
Question 2
Cette fois, on cherche à calculer la moyenne des notes se trouvant dans le fichier. Que peut-on dire sur l'espace mémoire occupé par ce programme ?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 | #!/usr/bin/env python3
import sys
def traite_fichier(nom_fichier):
fichier = open(nom_fichier, "r")
resultats = []
for ligne in fichier:
ligne_decoupee = ligne.split(" ")
prenom = ligne_decoupee[0]
note = int(ligne_decoupee[1])
resultats.append((prenom, note))
fichier.close()
return resultats
def calcule_moyenne(prenoms_notes):
somme = 0
nb_etudiants = 0
for resultat in prenoms_notes:
somme += resultat[1]
nb_etudiants += 1
return somme / nb_etudiants
def teste():
"""Teste les fonctions ci-dessus."""
if len(sys.argv) != 2:
print("Usage :", sys.argv[0], "nom_fichier")
else:
prenoms_notes = traite_fichier(sys.argv[1])
print("La note moyenne est", calcule_moyenne(prenoms_notes))
if __name__ == "__main__":
teste()
|
Question 3
Comment corriger le problème de calculs inutiles et de mémoire dans le cas de la recherche d'un étudiant uniquement avec ce que nous avons vu jusqu'ici, c'est-à-dire sans avoir recours à yield
?
Question 4
Quel est l'inconvénient de cette solution ?
Exercice 2 : il n'y a pas de problème, il n'y a que des solutions
Voici donc comment, à l'aide de l'utilisation d'un yield
Python, avoir une recherche d'étudiant dans laquelle :
- nous ne faisons aucun calcul pour rien ;
- nous ne créons pas de
list
d'étudiants, donc utilisation mémoire constante ;
- le traitement du fichier et la recherche sont séparés dans deux fonctions.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 | #!/usr/bin/env python3
import sys
def traite_fichier(fichier):
for ligne in fichier: # tf1 (bof, non ?)
ligne_decoupee = ligne.split(" ") # tf2
prenom = ligne_decoupee[0] # tf3
note = int(ligne_decoupee[1]) # tf4
yield (prenom, note) # tf5
def cherche_note(prenom, resultats):
for resultat in resultats: # cn1
if resultat[0] == prenom: # cn2
return resultat[1] # cn3
return None # cn4
def get_note():
if len(sys.argv) != 3: # gn1
print("Usage :", sys.argv[0], "nom_fichier prenom") # gn2
return # gn3
fichier = open(sys.argv[1], "r") # gn4
iterateur_prenoms_notes = traite_fichier(fichier) # gn5
prenom = sys.argv[2] # gn6
note = cherche_note(prenom, iterateur_prenoms_notes) # gn7
print("La note de", prenom, "est", note) # gn8
fichier.close() # gn9
if __name__ == "__main__": # 1
get_note() # 2
|
Question 1
Analyser attentivement le code ci-dessus.
En essayant de "deviner" ce que fait le yield
, prendre quelques minutes pour dérouler le programme sur papier en écrivant les numéros de lignes successivement exécutées dans le cas d'un appel correct, c'est à dire avec un fichier qui existe et un nom d'étudiant qui existe en troisième position du fichier.
Question 2
À l'aide d'un générateur, réécrire le programme qui calcule la moyenne des notes.
Quel est l'avantage par rapport au calcul de moyenne de l'exercice 1 ?
Exercice 3 : générer les jours de la semaine.
Question 1
Écrire une fonction génératrice renvoyant un itérateur sur les chaînes de caractères représentant les jours de la semaine.
Question 2
Qu'affiche le programme suivant ?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 | #!/usr/bin/env python3
"""Petit exemple pour illustrer le contexte de CHAQUE itérateur 1 et 2"""
def get_first_five():
"""Fonction génératrice des 5 premiers nombres."""
yield "one"
yield "two"
yield "three"
yield "four"
yield "five"
def affiche():
"""Affiche des trucs en utilisant DEUX itérateurs."""
iterateur_1 = get_first_five()
iterateur_2 = get_first_five()
print("De l'itérateur 1", next(iterateur_1))
print("De l'itérateur 1", next(iterateur_1))
print("De l'itérateur 2", next(iterateur_2))
print("De l'itérateur 1", next(iterateur_1))
if __name__ == "__main__":
affiche()
|
Exercice 4 : analyse de code
Question 1
Qu'affiche le programme ci-dessous ?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 | #!/usr/bin/env python3
"""Train reading someone else (bad) code"""
def mystery_function(first_parameter, second_parameter, everything):
"""What am I doing?"""
for variable in first_parameter:
everything.append(variable[second_parameter])
yield variable[second_parameter]
def main():
"""Script's entry point"""
a_list = []
something = mystery_function([("to", 12), ("ti", 17), ("ta", 47)], 1, a_list)
print("type of something is", type(something))
for element in something:
print(element, end=" ")
print()
something_else = mystery_function(("to", "ti", "\u0634 what is that?"), 0, a_list)
print("type of something_else is", type(something_else))
for element in something_else:
print(element, end=" ")
print()
print(a_list)
other_thing = mystery_function({(1, 2, 3), (4, 5, 6), (7, 8)}, 2, a_list)
print("type of other_thing is", type(other_thing))
for element in other_thing:
print(element, end=" ")
print()
if __name__ == "__main__":
main()
|
Exercice 5 : quand aurions nous pu/dû utiliser yield
? (pour aller plus loin)
Question 1
Chercher dans ses notes, sa mémoire, son ordinateur, à quels endroits nous aurions pu/dû utiliser yield
dans le cadre des TD et TP BPI en justifiant pourquoi ?