Contexte

Python est un langage de programmation très abordable que l’on démarre en programmation ou que l’on connaisse déjà d’autres langages. Néanmoins, ce n’est pas parce qu’un langage est abordable qu’il n’a pas ses propres idiomes et qu’il n’y a pas des trucs & astuces à connaître et à retenir.

Ce billet est l’introduction d’une série de notes d’apprentissage de Python, sur la manière de traiter les séquences en Python dans un style fonctionnel. Ce que j’entends par là c’est l’utilisation de fonctions comme filter, map et reduce pour itérer sur une séquence et la transformer dans l’esprit du pattern collection pipeline plutôt que de passer par des itérations avec des boucles for ou while comme on le fait dans un style impératif. On verra également que c’est une alternative à l’utilisation des compréhensions.

Le créateur de Python, Guido van Rossum a clairement écrit que les principales influences de Python venaient de la programmation impérative. Il n’a jamais souhaité faire de Python un langage fonctionnel bien que les fonctions en elles-mêmes soit des objets de première classe. Néanmoins, face à la demande de la communauté, les fonctions map et ses associées filter (comme map, une fonction native) et reduce (initialement une fonction native mais maintenant dans le module functools) sont rapidement arrivées dans le langage ainsi que les lambdas.

L’objet de cette série est de voir notamment les bases de l’utilisation des fonctions filter, map et reduce, notamment dans des contextes similaires à ceux dans lesquels on utiliserait des compréhensions ou des boucles impératives. Cela sera également l’occasion de revenir sur le module itertools déjà évoqué dans les billets sur zip et les compréhensions. A partir de cette base sur filter/map/reduce, on pourra construire flatmap et voir ce qu’itertools propose pour compléter ces fonctions de base.

L’utilisation de filter/map/reduce en Python n’est pas considérée comme un style très pythonique : on lui préfère l’utilisation des compréhensions qui permettent de faire la même chose dans de très nombreux cas courants d’une manière considérée dans l’écosystème Python comme plus lisible.

Les fonctions filter/map/reduce sont néanmoins très communes dans les langages fonctionnelles classiques (parfois avec des noms différents) et ont fait leur entrée dans des langages mainstream comme Java ou Javascript depuis un moment. On retrouve également ces fonctions en tant qu’opérateurs de reactive streams (voir par exemple ReactiveX).

D’ailleurs dans les approches fonctionnelles, on voit souvent la programmation comme une transformation de données ou d’un flux de données. Et dans cette vision es fonctions filter/map/reduce sont parmi les opérateurs au coeur de ces transformations. Cette vision est au coeur du pattern collection pipeline.

Bref, il est fort possible que vous ayiez déjà entendu parler ou utilisé ces fonctions.

Cette série de billets est donc l’occasion d’explorer totu cela en commençant par l’utilisation des fonctions filter, map et reduce dans le contexte de Python, en les comparant à l’utilisation des compréhensions ou d’une approche impérative classique.

NB : La version de Python utilisée dans les exemples de code est la version 3.

A quoi cela sert-il ?

C’est bien beau de faire une longue introduction qui parle de filter/map/reduce et de la programmation fonctionnelle mais elles font quoi ces fonctions ? On y vient tout de suite.

En programmation fonctionnelles, on dit que ces fonctions sont des fonctions d’ordre supérieur (higher-order function). Une fonction d’ordre supérieur est une fonction qui prend en paramètre une autre fonction ou qui en retourne une.

Les fonctions filter et map prennent 2 paramètres : une fonction et une séquence. Le terme de séquence utilisé ici correspond en Python aussi bien à une liste, un tuple ou un itérateur : c’est une une suite d’éléments. Elles appliquent la fonction sur chacun des éléments de la séquence pour produire une nouvelle séquence.

La fonction reduce fonctionne sur un principe similaire, prenant en paramètre également une fonction, une séquence et éventuellement un troisième paramètre optionnel, appliquant la fonction sur les éléments de la séquence pour produire un résultat qui n’est pas nécessairement une séquence, dans le cas de reduce. On verra que filter et map sont des spécialisations de reduce.

Les fonctions filter/map/reduce sont des abstractions de traitement sur les séquences que l’on réalise dans des langages impératifs avec des boucles et des alternatives. Dans les langages fonctionnels ce sont les fonctions de bases pour manipuler les séquences d’éléments. Avec ces fonctions et le sucre syntaxique approprié, vous pouvez définir de véritable chaînes de traitement de données (on parle parfois de transformation pipeline ou de Collection Pipeline). Quand dans un contexte de Big Data on parle de Map/Reduce, on parle bien des mêmes principes que les fonctions ci-dessus, on n’est juste pas à la même échelle.

On verra que justement en Python on n’a pas le sucre syntaxique que l’on peut avoir dans certains langages et que cela demande de s’adapter pour rendre son code lisible.

On abordera également la fonction flatmap qui vient utilement compléter le trio précédent.

Filter, Map, Reduce et les autres

Ce billet était initialement prévu comme un billet unique et non comme une série. Cependant chemin faisant, j’ai réalisé qu’il serait plus pertinent de le découper en plusieurs billets pour en faire une série.

  • le premier billet de la série traite de filter
  • On passe ensuite à map
  • On enchaine sur reduce
  • Après avoir vu reduce, il sera temps de conclure la série sur flatmap.

Ressources