Les fonctions anonymes (lambda)

En Python, le mot-clé lambda est utilisé pour définir des fonctions anonymes, également appelées fonctions lambda.

Les fonctions lambda sont des fonctions qui ne sont pas définies avec un nom, mais qui peuvent être utilisées dans des expressions ou des fonctions.

Elles sont souvent utilisées dans des situations où une fonction simple est nécessaire pour une tâche spécifique, comme trier une liste ou filtrer des éléments.

Syntaxe d'une fonction anonyme

Les fonctions lambda avec Python sont créées selon la syntaxe suivante :

lambda arguments : expression

Les arguments sont les paramètres de la fonction et l'expression est le corps de la fonction.

La fonction anonyme renvoie la valeur de l'expression évaluée comme n'importe quelle fonction Python classique.

Remarquez l'absence d'utilisation du mot-clé return, contrairement à une fonction classique.

Voici un exemple simple un peu plus concret :

>>> square = lambda x : x ** 2
>>> square(2)
4

Dans cet exemple, nous créons une fonction lambda qui prend un argument x et renvoie x au carré.

L'appel de la fonction avec 2 en argument retourne ainsi le nombre 4 (2 au carré).

Pourquoi utiliser des fonctions anonymes (lambda) ?

Il y a plusieurs raisons pour lesquelles on peut vouloir utiliser des fonctions anonymes (lambda) en Python :

  • Simplification du code : Les fonctions lambda sont utiles pour écrire du code plus concis et lisible, en évitant d'avoir à créer des fonctions avec des noms et des définitions plus longues.
  • Flexibilité : Les fonctions lambda peuvent être utilisées pour des tâches ponctuelles qui ne nécessitent pas la création d'une fonction complète. Cela peut être particulièrement utile dans des situations où une fonction doit être passée en tant qu'argument à une autre fonction.
  • Programmation fonctionnelle : Les fonctions anonymes sont couramment utilisées en programmation fonctionnelle, où les fonctions sont traitées comme des valeurs et peuvent être utilisées pour effectuer des opérations telles que le filtrage, le tri ou la transformation des données.
  • Performance : Les fonctions lambda peuvent parfois offrir des performances supérieures à celles des fonctions définies avec un nom, car elles sont compilées de manière dynamique au moment de leur utilisation, plutôt que d'être définies et stockées en mémoire.

Quelques exemples pratiques de fonction lambda

Avec Python, on a recours aux fonctions lambda dans des situations très précises, notamment avec d'autres fonctions spécifiques comme sorted, map, filter et reduce.

Dans bien des cas, l'utilisation d'une compréhension de liste est désormais à privilégier par rapport aux fonctions map, filter et reduce. Mais il reste des cas de figure dans lesquels ces fonctions peuvent être utiles, notamment lors de l'utilisation avec d'autres librairies comme pandas.

Tri d'une liste

Les fonctions lambda peuvent être utilisées pour trier une liste selon une clé spécifique.

Par exemple, pour trier une liste de dictionnaires selon la valeur d'une clé particulière, on peut utiliser la syntaxe suivante :

>>> people = [{'name': 'Alice', 'age': 25}, {'name': 'Bob', 'age': 30}, {'name': 'Charlie', 'age': 20}]
>>> sorted(people, key=lambda x: x['age'])
[{'name': 'Charlie', 'age': 20}, {'name': 'Alice', 'age': 25}, {'name': 'Bob', 'age': 30}]

Une utilisation également très courante est d'utiliser la fonction len avec sorted et les fonctions anonymes :

>>> ma_liste = ['abcd', 'abc', 'a', 'ab', 'abcde']
>>> sorted(ma_liste, key=lambda x: len(x))
['a', 'ab', 'abc', 'abcd', 'abcde']

Filtrage de données

La fonction lambda peut également être utilisée pour filtrer des données en fonction de critères spécifiques. Par exemple, pour filtrer une liste de nombres pairs, on peut utiliser la syntaxe suivante :

>>> nombres = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> list(filter(lambda x: x % 2 == 0, nombres))
[2, 4, 6, 8, 10]

Comme indiqué précédemment, depuis l'apparition dans Python des listes en compréhension, on privilégiera désormais cette syntaxe :

>>> nombres = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> [x for x in nombres if x % 2 == 0]
[2, 4, 6, 8, 10]

De la même façon, on pourrait utiliser la fonction map et une fonction anonyme pour modifier les éléments d'une liste :

>>> nombres = [1, 2, 3, 4, 5]
>>> list(map(lambda x: x * 2, nombres))
[2, 4, 6, 8, 10]

Et avec une liste en compréhension :

>>> nombres = [1, 2, 3, 4, 5]
>>> [x * 2 for x in nombres]
[2, 4, 6, 8, 10]

Utilisation de lambda avec pandas

Comme nous l'avons vu, les fonctions anonymes peuvent encore trouver une utilité avec certaines librairies Python comme pandas (librairie de traitement de données).

Voici deux exemples d'utilisation des fonctions anonymes avec la méthode apply de pandas, qui permet de modifier les valeurs d'un tableau.

Calcul de la moyenne pondérée

Pour calculer la moyenne pondérée d'une colonne dans un DataFrame, on peut utiliser la fonction apply avec une fonction lambda.

Par exemple, si on a un DataFrame avec les colonnes "valeur" et "poids", on peut calculer la moyenne pondérée comme suit :

import pandas as pd

df = pd.DataFrame({'valeur': [10, 20, 30], 'poids': [2, 3, 1]})
moyenne_ponderee = df.apply(lambda row: (row['valeur'] * row['poids']).sum() / row['poids'].sum(), axis=1)

Ici, la fonction lambda calcule la moyenne pondérée pour chaque ligne du DataFrame en multipliant la valeur de chaque ligne par son poids, en faisant la somme des produits, puis en divisant par la somme des poids.

Conversion de valeurs en majuscules

Pour convertir toutes les valeurs d'une colonne dans un certain type (ici, en majuscules), on peut utiliser la fonction apply avec une fonction lambda.

Par exemple, si on a un DataFrame avec une colonne "nom", on peut convertir tous les noms en majuscules comme suit :

import pandas as pd

df = pd.DataFrame({'nom': ['Alice', 'Bob', 'Charlie']})
df['nom_majuscules'] = df['nom'].apply(lambda x: x.upper())

Ici, la fonction lambda est utilisée pour appliquer la méthode upper de Python à chaque valeur de la colonne "nom", convertissant ainsi toutes les valeurs en majuscules. La nouvelle colonne nom_majuscules est ensuite ajoutée au DataFrame.

Cas d'utilisation déconseillé de la fonction lambda

Bien que les fonctions lambda soient utiles en Python pour de nombreux cas d'utilisation, il y a certains cas où il est déconseillé de les utiliser.

  • Complexité excessive : si une fonction lambda devient trop complexe ou comporte de nombreux niveaux d'imbrication, il peut être difficile de la comprendre et de la déboguer. Dans ce cas, il est souvent préférable d'utiliser une fonction définie avec un nom pour améliorer la lisibilité du code.
  • Réutilisation : si une fonction lambda est utilisée plusieurs fois dans le code, il peut être préférable de la remplacer par une fonction définie avec un nom pour éviter la duplication de code.

N'utilisez pas de fonctions anonymes si vous les assignez à un nom !

Cette recommandation est édictée dans la PEP 8, une liste de bonnes pratiques suivies par l'écrasante majorité des développeurs Python.

# Ok 👍
def square(x): return x ** 2

# Pas ok 👎
square = lambda x: x ** 2

Premièrement, si vous nommez une fonction lambda, c'est probablement que vous contrevenez à ce que nous venons de dire (si vous devez réutiliser une fonction lambda, faites une fonction normale).

Ensuite, cela rend le débogage de vos scripts Python beaucoup plus complexe, car le nom de la fonction restera lambda pour l'interpréteur Python et non pas le nom de la variable à laquelle vous l'avez assigné.

Exemple :

>>> square = lambda x: x ** 2
>>> square
<function <lambda> at 0x1042fa7a0>

>>> def square(x): return x ** 2
>>> square
<function square at 0x10431ef20>