Séance 1 — Fondations
1. Effet de bord
Une fonction produit un effet de bord lorsqu'elle modifie quelque chose en dehors de sa propre valeur de retour : une variable globale, un objet mutable passé en argument, un fichier, l'affichage à l'écran…
score = 0
def marquer_point():
global score
score = score + 1 # effet de bord : modifie une variable globale
return score
global sont des signaux typiques d'effet de bord.2. Fonction pure & transparence référentielle
Une fonction est pure si elle ne produit aucun effet de bord, et si son résultat ne dépend que de ses arguments. Une fonction pure est référentiellement transparente : on peut remplacer l'appel par sa valeur de retour n'importe où, sans changer le comportement du programme.
def carre(x): # pure
return x * x
def carre_bavard(x): # impure : effet de bord (affichage)
print("calcul en cours...")
return x * x
3. Exercice 1 — Test express
# Fonction A
def double(x):
return 2 * x
# Fonction B
compteur = 0
def incrementer():
global compteur
compteur += 1
return compteur
# Fonction C
def ajoute_a_liste(liste, x):
liste.append(x)
return liste
# Fonction D
def moyenne(a, b):
return (a + b) / 2
Classe chacune des 4 fonctions (pure / impure) en justifiant.
Voir la réponse
B : impure — modifie une variable globale, le résultat dépend de l'état.
C : impure — mute la liste passée en argument.
D : pure — résultat entièrement déterminé par a et b.
4. Exercice 2 — Le détective (fonction passée en argument)
Écris une fonction générique capable de retrouver le premier élément d'une liste qui vérifie une condition quelconque, en passant la condition elle-même en argument.
def trouve_le_heros(liste, condition):
"""Renvoie le premier élément de liste qui vérifie condition,
ou None si aucun ne convient."""
...
def est_majeur(age):
return age >= 18
ages = [12, 15, 17, 19, 22, 8]
print(trouve_le_heros(ages, est_majeur)) # doit afficher 19
a) Complète avec une boucle. b) Teste avec une autre fonction condition. c) Teste avec une lambda directement en argument.
Voir la réponse
def trouve_le_heros(liste, condition):
for element in liste:
if condition(element):
return element
return None
print(trouve_le_heros(ages, lambda a: a > 20)) # 22
Une lambda est une fonction anonyme, écrite en une seule expression, pratique quand la condition n'est utilisée qu'une fois.
5. Exercice 3 — De trouve_le_heros à filter()
Python fournit filter(fonction, iterable) qui fait la même chose que trouve_le_heros, mais renvoie tous les éléments valides.
resultat = filter(est_majeur, ages)
print(resultat) # <filter object at 0x...>
print(list(resultat)) # liste des éléments
a) Pourquoi print(resultat) n'affiche-t-il pas une liste ? b) Utilise filter() + next() pour ne récupérer que le premier majeur.
Voir la réponse
premier_majeur = next(filter(est_majeur, ages))
print(premier_majeur) # 19
filter(condition, liste) généralise et optimise l'idée de trouve_le_heros.
6. Exercice 4 — La boîte à magie (vers map())
ingredients = ["aile de chauve-souris", "oeil de triton", "poil de dragon"]
Transforme chaque ingrédient en majuscules avec : a) une boucle for, b) une compréhension de liste, c) map().
Voir la réponse
# a) boucle
resultat = []
for ing in ingredients:
resultat.append(ing.upper())
# b) compréhension
resultat = [ing.upper() for ing in ingredients]
# c) map()
resultat = list(map(str.upper, ingredients))
map() sépare clairement la fonction de transformation de la structure parcourue, sans variable intermédiaire.
Exercices défis (bonus)
Le piège de la valeur par défaut mutable : que va afficher ce code ?
def ajoute_a_panier(article, panier=[]):
panier.append(article)
return panier
print(ajoute_a_panier("pomme"))
print(ajoute_a_panier("poire"))
Voir la réponse
panier=None puis créer la liste à l'intérieur.
Écris filtre_et_transforme(liste, condition, transformation) qui combine filter et map.
Voir la réponse
def filtre_et_transforme(liste, condition, transformation):
return [transformation(x) for x in liste if condition(x)]
Une fonction qui appelle random.random() est-elle pure ?