Les fonctions lambda existent depuis la version 1.0 de Python comme un moyen de pro­gram­ma­tion fonc­tion­nelle. Leur uti­li­sa­tion est désormais largement remplacée par d’autres tech­niques. Mais il existe toutefois encore quelques ap­pli­ca­tions spé­cia­li­sées que les pro­gram­meurs che­vron­nés de Python doivent connaître.

Que sont les fonctions lambda en Python ?

En langage Python, une « fonction lambda » est une fonction anonyme. Pour créer cette fonction lambda, il convient d’utiliser le mot-clé « lambda ». Cette ex­pres­sion se compose ainsi du mot-clé « lambda », suivi d’une liste d’arguments, de deux-points et d’une ex­pres­sion distincte (« ex­pres­sion »). À l’appel de la fonction lambda, l’ex­pres­sion est complétée par les arguments et évaluée :

lambda argument: expression

Les fonctions sont un élément de langage fon­da­men­tal de presque tous les langages de pro­gram­ma­tion et re­pré­sen­tent la plus petite unité de code réu­ti­li­sable. Ha­bi­tuel­le­ment, les fonctions Python sont définies avec le mot-clé « def ». Voici par exemple la fonction carré, qui multiplie un nombre par lui-même :

# Définir la fonction carré
def square(num):
    return num * num
# Démontrer que cela fonctionne
assert square(9) == 81
python

Outre la méthode connue de dé­fi­ni­tion des fonctions en Python à l’aide du mot-clé « def », ce langage utilise également les « lambdas ». Il s’agit de fonctions courtes et anonymes (sans nom) qui dé­fi­nis­sent une ex­pres­sion à l’aide de pa­ra­mètres. Les lambdas peuvent être utilisées partout où une fonction est attendue ou peuvent être associées à un nom par as­sig­na­tion. Voici l’équi­valent en version lambda de la fonction carré sus­men­tion­née :

# Créer la fonction carré
squared = lambda num: num * num
# Démontrer que cela fonctionne
assert squared(9) == 81
python
Note

En langage Python, le terme « fonction lambda » désigne une fonction générée à l’aide du mot-clé lambda. Lambda n’est pas le nom d’une fonction et il ne s’agit pas non plus de l’un des opé­ra­teurs Python.

Quelle est la dif­fé­rence entre lambda et def ?

De prime abord, il semble étrange que Python, avec « lambda » et « def », dispose de deux moyens de créer des fonctions. Pourtant, « lambda » n’est pas une fonction à pro­pre­ment parler, mais seulement une autre or­tho­graphe pour créer lo­ca­le­ment de courtes fonctions. Chaque fonction ayant été créée avec « lambda » est en effet également générée via « def ». L’inverse n’est toutefois pas vrai.

Au niveau syn­taxique, « lambda » et « def » sont tous deux des mots-clés. La dif­fé­rence entre les deux réside toutefois dans la sé­pa­ra­tion stricte entre une ins­truc­tion (« statement ») et une ex­pres­sion (« ex­pres­sion ») en langage Python. Pour résumer, les ins­truc­tions sont des étapes dans l’exécution du code tandis que les ex­pres­sions sont évaluées selon une valeur.

Le mot-clé « def » débute une ins­truc­tion (spé­ci­fi­que­ment une « compound statement ») qui contient d’autres ins­truc­tions. Au sein de l’ins­truc­tion « def », des ins­truc­tions « return » peuvent ap­pa­raître. Lors de l’appel d’une fonction définie avec « def », une ins­truc­tion « return » retourne une valeur.

Con­trai­re­ment à l’ins­truc­tion « def », le mot-clé « lambda » débute une ex­pres­sion qui ne doit pas contenir d’ins­truc­tions. L’ex­pres­sion lambda accepte un ou plusieurs arguments et retourne une fonction anonyme. Si la fonction lambda créée est appelée, l’ex­pres­sion contenue est évaluée avec les arguments transmis, puis est retournée.

Quelles sont les res­tric­tions des ex­pres­sions lambda de Python ?

Python restreint de manière ciblée les uti­li­sa­tions des fonctions lambda, car en règle générale, il est pré­fé­rable de nommer les fonctions. Cela oblige les pro­gram­meurs à réfléchir au sens de la fonction et à clai­re­ment délimiter les dif­fé­rentes parties.

Con­trai­re­ment au corps d’une fonction définie via le mot-clé « def », les lambdas ne peuvent contenir aucune ins­truc­tion. Il est donc im­pos­sible d’utiliser « if », « for », etc. dans une fonction lambda. Même le dé­clen­che­ment d’une exception est im­pos­sible, car l’ins­truc­tion « raise » est requise pour cela.

Les fonctions lambda en Python ne doivent contenir qu’une ex­pres­sion distincte qui sera évaluée lors de l’appel. Dans l’ex­pres­sion lambda, aucune an­no­ta­tion de type ne peut être utilisée. Désormais, d’autres tech­niques sont employées dans la majorité des cas d’uti­li­sa­tion des fonctions lambda en langage Python. On peut ici en par­ti­cu­lier citer les listes en com­pré­hen­sion.

Pourquoi les fonctions lambda sont-elles utilisées en Python ?

En règle générale, les lambdas relèvent de la pro­gram­ma­tion fonc­tion­nelle. Dans certains langages, comme Ja­vaS­cript, les fonctions anonymes sont souvent utilisées sans qu’un mot-clé spé­ci­fique ne soit employé. En Python, les ex­pres­sions lambda servent à générer lo­ca­le­ment de petites fonctions sans tout ce qui les ac­com­pagne. Nous vous pré­sen­tons les cas d’uti­li­sa­tion les plus utiles.

Insérer des lambdas dans les fonctions d’ordre supérieur en Python

Les lambdas sont souvent utilisées en com­bi­nai­son avec des fonctions d’ordre supérieur telles que « map() », « filter() » et « reduce() ». Grâce à elles, les éléments d’un objet « itérable » peuvent être trans­for­més sans utiliser de boucle. Les fonctions d’ordre supérieur (ou « higher order functions » en anglais) sont définies comme des fonctions acceptant d’autres fonctions en tant que paramètre ou qui renvoie une fonction.

La fonction « map() » accepte une fonction et un itérable comme pa­ra­mètres et exécute la fonction pour chaque élément de l’objet itérable. Examinons le problème que constitue la gé­né­ra­tion de nombres carrés : nous utilisons la fonction « map() » et indiquons une ex­pres­sion lambda comme argument, qui génère la fonction carré. Avec « map() », la fonction carré est appliquée à tous les éléments de la liste :

nums = [3, 5, 7]
# Nombres carrés à l’aide de « map() » et « lambda »
squares = map(lambda num: num ** 2, nums)
# Démontrer que cela fonctionne
assert list(squares) == [9, 25, 49]
python
Note

Depuis la version Python 3.0, les fonctions « map() » et « filter() » renvoient un itérable au lieu d’une liste. Dans l’ins­truc­tion « assert », nous utilisons un appel « list() » afin d’extraire l’itérable dans une liste.

Grâce aux listes en com­pré­hen­sion, il existe désormais une approche plus moderne et pri­vi­lé­giée pour traiter les itérables. Au lieu d’utiliser « map() » et de générer une fonction lambda, nous décrivons di­rec­te­ment l’opération :

nums = [3, 5, 7]
# Nombres carrés à l’aide de la liste en compréhension
squares = [num ** 2 for num in nums]
# Démontrer que cela fonctionne
assert squares == [9, 25, 49]
python

Grâce à la fonction « filter() », vous pouvez filtrer les éléments d’un itérable. Nous dé­ve­lop­pons notre exemple de façon que seuls des nombres carrés précis soient générés :

# Liste de chiffres de 1 à 4
nums = [1, 2, 3, 4]
# Carré de chaque chiffre
squares = list(map(lambda num: num ** 2, nums))
# Filtrer les carrés pairs
even_squares = filter(lambda square: square % 2 == 0, squares)
# Démontrer que cela fonctionne
assert list(even_squares) == [4, 16]
python

Nous pré­sen­tons à nouveau l’approche moderne pri­vi­lé­giée, à l’aide d’une liste en com­pré­hen­sion, pour générer le même résultat sans recourir aux lambdas ni aux fonctions d’ordre supérieur. Nous utilisons donc la partie « if » de la com­pré­hen­sion pour filtrer les éléments pairs des chiffres carrés obtenus :

# Liste de chiffres de 1 à 4 au carré
squares = [num ** 2 for num in range(1, 5)]
# Filtrer les carrés pairs
even_squares = [square for square in squares if square % 2 == 0]
# Démontrer que cela fonctionne
assert even_squares == [4, 16]
python
Note

La fonction « reduce() » de Python ne fait plus partie de la bi­blio­thèque standard depuis la version Python 3.0. Elle a été déplacée dans le module « functools ».

Réaliser des fonctions de clé en Python, comme des lambdas

Les com­pré­hen­sions ont ma­jo­ri­tai­re­ment remplacé l’usage des fonctions d’ordre supérieur clas­siques « map() » et « filter() ». Grâce aux « fonctions de clé » (« key functions » en anglais), il existe toutefois un scénario d’ap­pli­ca­tion dans lequel les points forts des lambdas sont plei­ne­ment mis à profit.

Les fonctions de com­pa­rai­son Python « sorted() », « min() » et « max() » fonc­tion­nent sur la base d’itérables. Lors de l’appel, chaque élément de l’itérable est soumis à une com­pa­rai­son. Chacune des trois fonctions accepte une fonction de clé comme paramètre « key » optionnel. La fonction de clé est appelée pour chaque élément et retourne une valeur de clé pour la com­pa­rai­son.

Examinons le problème suivant à titre d’exemple. Nous avons un dossier contenant des fichiers image dont les noms sont re­pro­duits dans une liste Python. Nous sou­hai­tons trier cette liste. Les noms des fichiers com­men­cent tous par « img » et sont suivis d’une nu­mé­ro­ta­tion :

# Liste des noms des fichiers image
images = ['img1', 'img2', 'img30', 'img3', 'img22', 'img100']
python

Si nous utilisons la fonction « sorted() » de Python, « l’ordre lexi­co­gra­phique » est utilisé. Cela signifie que les chiffres sé­quen­tiels sont traités en tant que chiffres uniques. Ainsi, les numéros « [‘1’, ‘2’, ‘100’] » sont présentés dans l’ordre « [‘1’, ‘100’, ‘2’] ». Le résultat ne cor­res­pond pas à nos attentes :

# Trier selon l’ordre lexicographique
sorted_image = sorted(images)
# Démontrer que cela fonctionne
assert sorted_image == ['img1', 'img100', 'img2', 'img22', 'img3', 'img30']
python

Afin de con­fi­gu­rer le tri selon notre souhait, nous trans­met­tons une ex­pres­sion « lambda » qui génère une fonction de clé. La fonction de clé extrait la partie numérique du nom d’un fichier, qui sera ensuite utilisée comme clé par la fonction « sorted() » :

# Extraire le composant numérique et trier en tant qu’entiers
sorted_image = sorted(images, key=lambda name: int(name[3:]))
# Démontrer que cela fonctionne
assert sorted_image == ['img1', 'img2', 'img3', 'img22', 'img30', 'img100']
python

La fonction de clé est utilisée lo­ca­le­ment uni­que­ment et une seule fois. Il n’est pas né­ces­saire de définir à part une fonction nommée. Les lambdas sont ainsi le moyen approprié pour créer des fonctions de clé. Examinons deux autres exemples.

Outre « sorted() », les fonctions intégrées de Python « min() » et « max() » acceptent une fonction de clé op­tion­nelle. Les fonctions trouvent le plus petit ou le plus grand élément d’une liste ou d’un autre itérable. Ce qui constitue pré­ci­sé­ment le plus petit ou le plus grand élément est une question de dé­fi­ni­tion et est déterminé via la fonction de clé.

Dans une liste de valeurs simples, comme une liste de chiffres, ce que l’on entend par élément « le plus petit » ou « le plus grand » est évident. Aucune fonction de clé spéciale n’est ici requise :

nums = [42, 69, 51, 13]
assert min(nums) == 13
assert max(nums) == 69
python
Note

Si aucune fonction de clé n’est transmise, la fonction d’identité « f(x) = x » est utilisée de manière implicite. Cette fonction est aussi simple à définir que la lambda Python avec « lambda x:x ».

Qu’en est-il alors lorsque les éléments d’un itérable con­tien­nent res­pec­ti­ve­ment plusieurs données ? Imaginons une liste de dic­tion­naires qui re­pré­sen­tent des personnes, avec leur nom et leur âge. D’après quels critères « min() » et « max() » doivent-ils dé­ter­mi­ner quels éléments sont les plus petits ou les plus grands ? C’est là que la fonction de clé entre en jeu.

Pour illustrer le fonc­tion­ne­ment des fonctions de clé, nous avons besoin de données d’exemple. Nous créons une fonction « Personne() » servant de cons­truc­teur :

# Fonction de constructeur pour le dictionnaire représentant une personne
def Person(name, age):
    return {'name': name, 'age': age}
# Vérifier que cela fonctionne tel que prévu
assert Person('Jim', 42) == {'name': 'Jim', 'age': 42}
python

Grâce à notre fonction de cons­truc­teur, nous créons une liste de personnes :

# Créer un liste de personnes
people = [Person('Jim', 42), Person('Jack', 51), Person('John', 69)]
python

Fi­na­le­ment, nous trouvons la personne la plus âgée via l’appel « max() ». Nous créons ainsi, via une ex­pres­sion lambda, une fonction de clé, qui accepte un dic­tion­naire de personnes et en extrait l’âge comme un élément de com­pa­rai­son :

# Trouver la personne la plus âgée
oldest = max(people, key=lambda person: person['age'])
# Vérifier que cela fonctionne
assert oldest == Person('John', 69)
python

L’approche fonc­tionne de la même manière pour la fonction « min() ». Nous dé­fi­nis­sons ici la fonction de clé en dehors de l’appel « min() », mais nous utilisons à nouveau l’ex­pres­sion lambda. Cela améliore la li­si­bi­lité et est avan­ta­geux lorsque la fonction de clé est utilisée lo­ca­le­ment de nom­breuses fois :

# Définir la fonction de clé pour comparer les personnes selon leur âge
by_age = lambda person: person['age']
# Trouver la personne la plus jeune
youngest = min(people, key=by_age)
# Vérifier que cela fonctionne
assert youngest == Person('Jim', 42)
python

Créer des fer­me­tures avec les lambdas Python

Les lambdas Python sont également utilisées dans la dé­fi­ni­tion des fer­me­tures (ou « closures » en anglais). Il s’agit là de fonctions créées à partir d’autres fonctions et qui en­re­gistrent à cet effet une valeur. Les fer­me­tures per­met­tent par exemple de créer des familles de fonctions si­mi­laires. Voici un exemple courant ex­pli­quant la création de fonctions de puissance.

Les fonctions de puissance acceptent un argument et l’élèvent à la puissance cor­res­pon­dante. Les exemples connus sont la fonction carré « f(x) = x ^ 2 » et la fonction cube « f(x) = x ^ 3 ». Une fonction de cons­truc­teur permet de créer des fonctions de puissance quel­conques en tant que fer­me­tures. Nous utilisons une ex­pres­sion lambda et évitons ainsi de définir une fonction nommée interne :

# Définir la fonction de constructeur pour les fonctions de puissance
def power(n):
    return lambda num: num ** n
# Créer les fonctions carré et cube comme fermetures
square = power(2)
cubic = power(3)
# Démontrer que cela fonctionne
assert square(10) == 100
assert cubic(10) == 1000
python

Ex­pres­sion de fonction im­mé­dia­te­ment invoquée (IIFE) avec les lambdas Python

Les IIFE (pour Im­me­dia­tely Invoked Function Ex­pres­sion ; prononcez « ayfi ») sont un modèle connu dans Ja­vaS­cript. Une fonction anonyme est ainsi définie et im­mé­dia­te­ment exécutée.

Lorsque cela s’avère peu utile même avec la li­mi­ta­tion dans Python, les lambdas peuvent être utilisées comme IIFE. Nous avons seulement besoin de pa­ren­thèses autour de l’ex­pres­sion lambda :

(lambda num: num * num)
python

Ainsi que d’une autre paire de pa­ren­thèses entourant le ou les arguments :

assert (lambda num: num * num)(3) == 9
python
Conseil

Nous con­seil­lons aux débutants de consulter notre tutoriel Python.

Aller au menu principal