🔝 Retour au Sommaire
Dans ce chapitre, nous allons découvrir deux concepts importants de la programmation fonctionnelle en Python : les fonctions lambda (ou fonctions anonymes) et les fonctions d'ordre supérieur. Ces concepts permettent d'écrire du code plus concis et expressif.
Ne vous inquiétez pas si ces termes semblent compliqués, nous allons les découvrir étape par étape avec des exemples simples !
Une fonction lambda est une petite fonction anonyme (sans nom) qui peut être définie en une seule ligne. Elle est particulièrement utile pour des opérations simples que vous n'avez besoin d'utiliser qu'une seule fois.
lambda arguments: expressionlambda: le mot-clé pour déclarer une fonction lambdaarguments: les paramètres de la fonction (comme dans une fonction normale)expression: une seule expression dont le résultat sera retourné automatiquement
Prenons un exemple simple : une fonction qui additionne deux nombres.
Avec une fonction classique :
def additionner(a, b):
return a + b
resultat = additionner(5, 3)
print(resultat) # Affiche : 8 Avec une fonction lambda :
additionner = lambda a, b: a + b
resultat = additionner(5, 3)
print(resultat) # Affiche : 8 Les deux approches donnent le même résultat, mais la lambda est plus concise.
doubler = lambda x: x * 2
print(doubler(5)) # Affiche : 10
print(doubler(12)) # Affiche : 24 est_pair = lambda n: n % 2 == 0
print(est_pair(4)) # Affiche : True
print(est_pair(7)) # Affiche : False saluer = lambda prenom, nom: f"Bonjour {prenom} {nom} !"
print(saluer("Marie", "Dupont")) # Affiche : Bonjour Marie Dupont !maximum = lambda a, b: a if a > b else b
print(maximum(10, 25)) # Affiche : 25
print(maximum(50, 30)) # Affiche : 50 ✅ Utilisez les lambda quand :
- Vous avez besoin d'une fonction simple et courte
- La fonction n'est utilisée qu'une seule fois
- Vous passez la fonction comme argument à une autre fonction
❌ Évitez les lambda quand :
- La logique est complexe (plusieurs lignes nécessaires)
- Vous devez réutiliser la fonction à plusieurs endroits
- Le code devient difficile à lire
Une fonction d'ordre supérieur est une fonction qui peut :
- Prendre une ou plusieurs fonctions comme arguments
- Retourner une fonction comme résultat
Ce concept est au cœur de la programmation fonctionnelle et permet de créer du code très flexible et réutilisable.
C'est le cas le plus courant. Voici un exemple simple :
def appliquer_operation(nombre, operation):
"""Applique une opération sur un nombre."""
return operation(nombre)
# Utilisation avec différentes opérations
doubler = lambda x: x * 2
tripler = lambda x: x * 3
carre = lambda x: x ** 2
print(appliquer_operation(5, doubler)) # Affiche : 10
print(appliquer_operation(5, tripler)) # Affiche : 15
print(appliquer_operation(5, carre)) # Affiche : 25 Dans cet exemple, appliquer_operation est une fonction d'ordre supérieur car elle prend une fonction (operation) comme argument.
Voici un exemple où une fonction crée et retourne une autre fonction :
def creer_multiplicateur(n):
"""Crée une fonction qui multiplie par n."""
return lambda x: x * n
# Création de différentes fonctions
multiplier_par_2 = creer_multiplicateur(2)
multiplier_par_5 = creer_multiplicateur(5)
multiplier_par_10 = creer_multiplicateur(10)
# Utilisation
print(multiplier_par_2(7)) # Affiche : 14
print(multiplier_par_5(7)) # Affiche : 35
print(multiplier_par_10(7)) # Affiche : 70 Ici, creer_multiplicateur retourne une fonction lambda différente selon la valeur de n.
Créons une fonction d'ordre supérieur pour filtrer une liste selon un critère :
def filtrer(liste, condition):
"""Filtre une liste selon une condition."""
resultat = []
for element in liste:
if condition(element):
resultat.append(element)
return resultat
# Liste de nombres
nombres = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# Filtrer les nombres pairs
pairs = filtrer(nombres, lambda x: x % 2 == 0)
print(f"Nombres pairs : {pairs}") # Affiche : [2, 4, 6, 8, 10]
# Filtrer les nombres supérieurs à 5
superieurs_a_5 = filtrer(nombres, lambda x: x > 5)
print(f"Nombres > 5 : {superieurs_a_5}") # Affiche : [6, 7, 8, 9, 10]
# Filtrer les multiples de 3
multiples_de_3 = filtrer(nombres, lambda x: x % 3 == 0)
print(f"Multiples de 3 : {multiples_de_3}") # Affiche : [3, 6, 9] def transformer(liste, transformation):
"""Applique une transformation à chaque élément d'une liste."""
resultat = []
for element in liste:
resultat.append(transformation(element))
return resultat
nombres = [1, 2, 3, 4, 5]
# Doubler chaque nombre
doubles = transformer(nombres, lambda x: x * 2)
print(f"Doublés : {doubles}") # Affiche : [2, 4, 6, 8, 10]
# Mettre au carré
carres = transformer(nombres, lambda x: x ** 2)
print(f"Carrés : {carres}") # Affiche : [1, 4, 9, 16, 25]
# Transformer en chaînes
chaines = transformer(nombres, lambda x: f"Numéro {x}")
print(f"Chaînes : {chaines}") # Affiche : ['Numéro 1', 'Numéro 2', ...] Les fonctions d'ordre supérieur permettent de composer des fonctions :
def composer(f, g):
"""Crée une nouvelle fonction qui applique g puis f."""
return lambda x: f(g(x))
# Fonctions de base
ajouter_5 = lambda x: x + 5
multiplier_par_2 = lambda x: x * 2
# Composition : d'abord multiplier par 2, puis ajouter 5
fonction_composee = composer(ajouter_5, multiplier_par_2)
print(fonction_composee(3)) # (3 * 2) + 5 = 11
print(fonction_composee(10)) # (10 * 2) + 5 = 25 Les fonctions lambda sont très utiles pour trier des listes avec des critères personnalisés :
# Liste de personnes
personnes = [
{"nom": "Alice", "age": 30},
{"nom": "Bob", "age": 25},
{"nom": "Charlie", "age": 35},
{"nom": "David", "age": 28}
]
# Trier par âge
personnes_par_age = sorted(personnes, key=lambda p: p["age"])
print("Triées par âge :")
for p in personnes_par_age:
print(f" {p['nom']} : {p['age']} ans")
# Trier par nom
personnes_par_nom = sorted(personnes, key=lambda p: p["nom"])
print("\nTriées par nom :")
for p in personnes_par_nom:
print(f" {p['nom']} : {p['age']} ans")# Liste de produits avec leurs prix
produits = [
{"nom": "Pomme", "prix": 2.5},
{"nom": "Banane", "prix": 1.8},
{"nom": "Orange", "prix": 3.2},
{"nom": "Poire", "prix": 2.9}
]
# Fonction d'ordre supérieur pour calculer une réduction
def appliquer_reduction(produits, calculer_nouveau_prix):
"""Applique une réduction sur tous les produits."""
return [
{
"nom": p["nom"],
"prix_original": p["prix"],
"prix_reduit": calculer_nouveau_prix(p["prix"])
}
for p in produits
]
# Réduction de 20%
avec_reduction = appliquer_reduction(
produits,
lambda prix: prix * 0.8
)
print("Produits avec réduction de 20% :")
for p in avec_reduction:
print(f"{p['nom']}: {p['prix_original']}€ → {p['prix_reduit']:.2f}€")def valider_donnees(donnees, validateurs):
"""Vérifie si les données passent tous les validateurs."""
for validateur in validateurs:
if not validateur(donnees):
return False
return True
# Validateurs pour un mot de passe
validateurs_mdp = [
lambda mdp: len(mdp) >= 8, # Au moins 8 caractères
lambda mdp: any(c.isupper() for c in mdp), # Au moins une majuscule
lambda mdp: any(c.isdigit() for c in mdp), # Au moins un chiffre
]
# Tests
mot_de_passe_1 = "Password123"
mot_de_passe_2 = "faible"
print(f"'{mot_de_passe_1}' est valide : {valider_donnees(mot_de_passe_1, validateurs_mdp)}")
print(f"'{mot_de_passe_2}' est valide : {valider_donnees(mot_de_passe_2, validateurs_mdp)}") ✅ Concision : Permet d'écrire du code court pour des opérations simples
✅ Lisibilité : Dans certains contextes (tri, filtrage), rend le code plus clair
✅ Pas de pollution : Ne nécessite pas de définir une fonction nommée pour un usage unique
❌ Une seule expression : On ne peut pas écrire de code sur plusieurs lignes
❌ Pas de documentation : Impossible d'ajouter une docstring
❌ Débogage difficile : Les erreurs sont plus difficiles à identifier
❌ Lisibilité réduite : Si l'expression devient trop complexe
✅ Réutilisabilité : Permet de créer du code générique et flexible
✅ Abstraction : Sépare la logique d'itération de la logique métier
✅ Composition : Permet de combiner des fonctions simples pour créer des comportements complexes
# ✅ Bon : lambda simple et claire
nombres = [1, 2, 3, 4, 5]
pairs = list(filter(lambda x: x % 2 == 0, nombres))
# ❌ Mauvais : lambda trop complexe
resultat = list(map(lambda x: x * 2 if x % 2 == 0 else x * 3 if x % 3 == 0 else x, nombres))# ❌ Difficile à lire
produits_filtres = filter(
lambda p: p['prix'] < 50 and p['stock'] > 0 and p['categorie'] == 'electronique',
produits
)
# ✅ Plus clair
def est_produit_disponible(produit):
"""Vérifie si un produit est disponible et abordable."""
return (
produit['prix'] < 50 and
produit['stock'] > 0 and
produit['categorie'] == 'electronique'
)
produits_filtres = filter(est_produit_disponible, produits)def appliquer_a_tous(liste, fonction):
"""
Applique une fonction à tous les éléments d'une liste.
Args:
liste: La liste d'éléments à traiter
fonction: La fonction à appliquer à chaque élément
Returns:
Une nouvelle liste avec les résultats
"""
return [fonction(element) for element in liste]Dans ce chapitre, nous avons découvert :
Les fonctions lambda :
- Syntaxe :
lambda arguments: expression - Fonctions anonymes d'une seule ligne
- Utiles pour des opérations simples et ponctuelles
- À utiliser avec modération pour maintenir la lisibilité
Les fonctions d'ordre supérieur :
- Fonctions qui prennent d'autres fonctions en arguments
- Fonctions qui retournent d'autres fonctions
- Permettent de créer du code flexible et réutilisable
- Facilitent la composition et l'abstraction
Points clés à retenir :
- Les lambda sont pratiques mais doivent rester simples
- Les fonctions d'ordre supérieur augmentent la flexibilité du code
- Privilégiez la lisibilité : si une lambda devient complexe, utilisez une fonction classique
- Ces concepts sont fondamentaux en programmation fonctionnelle
Dans le prochain chapitre, nous verrons comment utiliser les fonctions d'ordre supérieur natives de Python : map(), filter(), et reduce().