Ordre supérieur et fonctions lambda

Passage de fonction en paramètre (ordre supérieur)

Jusqu'ici, on a vu des fonctions qui prennent en argument des nombres ou des listes, comme :

In [5]:
def f(x):
    return 2 * x

En Python, on peut passer une fonction en paramètre d'une autre fonction. Par exemple :

In [6]:
def applique_deux_fois(fct, a):
    tmp = fct(a)
    return fct(tmp)

Ici fct est un paramètre de applique_deux_fois de type fonction. Pour passer f comme paramètre effectif de fct, on écrit simplement :

In [7]:
applique_deux_fois(f, 4)
Out[7]:
16

On a bien f(f(4)) == 16.

Une fonction qui prend en paramètre ou renvoit une fonction est dite d'ordre supérieur. f est d'ordre 1, applique_deux_fois est d'ordre 2.

Bien sûr, comme pour les fonctions d'ordre 1, le paramètre effectif (celui passé au moment de l'appel) peut avoir le même nom que le paramètre formel (celui de la définition) :

In [8]:
def applique_trois_fois(f, x):
    tmp1 = f(x)
    tmp2 = f(tmp1)
    tmp3 = f(tmp2)
    return tmp3
In [9]:
applique_trois_fois(f, 2)
Out[9]:
16
In [10]:
import math
applique_trois_fois(math.sin, 2)
Out[10]:
0.7097000402345258
In [11]:
math.sin(math.sin(math.sin(2)))
Out[11]:
0.7097000402345258

Exemple : parcours de liste générique

On définit la fonction applique_partout qui applique une fonction à chaque élément d'une liste :

In [12]:
def applique_partout(f, lst):
    res = []
    for e in lst:
        res.append(f(e))
    return res
        
x = [1, 2, 10]
In [13]:
applique_partout(f, x)
Out[13]:
[2, 4, 20]

En fait, cette fonction existe déjà en Python, elle s'appelle map :

In [14]:
map(f, x)
Out[14]:
[2, 4, 20]

En Python, on a aussi une autre syntaxe, souvent plus jolie, pour map :

In [15]:
[f(a) for a in x]
Out[15]:
[2, 4, 20]

Fonctions et affectations

On peut affecter une fonction à une variable comme avec les autres valeurs :

In [16]:
ma_fonction = f
ma_fonction(4)
Out[16]:
8

Fonctions anonymes : fonctions lambda

Quand on définit une fonction avec

def f(x):
    return x * 2

on fait deux choses : on crée l'objet « fonction qui a x associe x + 1 », et l'affecte à une variable (globale) f.

On peut aussi créer une fonction sans lui donner de nom : c'est une fonction lambda :

In [17]:
f(3)
Out[17]:
6
In [18]:
(lambda x: x * 2)(3)
Out[18]:
6

(lambda x: x * 2) se lit « Fonction qui a x associe x * 2 » (Parfois notée $x \mapsto 2x$ chez les mathématiciens)

In [19]:
g = lambda x: x * 2  # équivalent à def g(x): return x * 2, mais plus court.
g(3)
Out[19]:
6

Les fonctions lambda sont surtout utiles pour passer une fonction en paramètre à une autre :

In [20]:
map(lambda x: x + 1, [1, 3, 42])
Out[20]:
[2, 4, 43]

Une fonction lambda peut avoir plusieurs paramètres :

In [21]:
somme = lambda x, y: x + y
somme(10, 3)
Out[21]:
13

Limitations des fonctions lambda

Pour éviter la tentation de code illisible, Python limite les fonctions lambda :

  • Une seule ligne

  • return implicite

Si on veut écrire des choses plus compliquées, on utilise def (on peut toujours).

Même avec ces limitations, on peut souvent s'en sortir. Par exemple :

In [22]:
def abs_avec_def(x):
    if x >= 0:
        return x
    else:
        return -x
map(abs_avec_def, [-1, 0, 42])
Out[22]:
[1, 0, 42]
In [23]:
map(lambda x: x if x >= 0 else -x, [-1, 0, 42])
Out[23]:
[1, 0, 42]