Dans le dernier article sur les lambdas, j’avais précisé que les lambdas étaient une question de style et qu’on pouvait faire sans. Voici différentes stratégies pour s’en passer.
Utiliser une inner function
En python on peut définir et exécuter à la volée une fonction dans une autre fonction/méthode ou une classe.
class Banane(object):
def blougou(): # pas de self ?
print "Sens giratoir inversé"
blougou() # on peut appeler une fonction en dehors d'une méthode
def methode(self): # une vraie méthode
print "Vive Edgar Morin"
# on peut définir une fonction dans une méthode
def moukrene():
print "A la glaviouse"
moukrene()
Et ça donne:
Sens giratoir inversé # affichage à l'import
>>> Banane().methode()
Vive Edgar Morin
A la glaviouse
Préremplissez des arguments
Le module functools
fournit tout un tas de goodies pour faire mumuse avec les fonctions, et notamment la fonction partial()
qui prend une fonction en argument, et des arguments à passer à la fonction qu’on lui a passé en argument. Vous suivez ?
On s’en sert quand on veut utiliser une fonction, mais qu’on sait d’avance quels arguments on va lui passer:
>>> from functools import partial
>>> sum
>>> sum([2, 2])
4
>>> mini_sum = partial(sum, [2, 2])
>>> mini_sum()
4
On pas besoin de donner tous les arguments de la fonction à enrober: on peut passer les autres après.
Comme partial()
retourne une fonction prête à être appelée, on peut l’utiliser là où on utiliserait une lambda:
>>> from __future__ import print_function
>>> def profit(etape1="On vole des caleçons", etape2=lambda: print('...')):
... print(etape1)
... etape2()
...
>>> profit()
On vole des caleçons
...
Se transforme en:
>>> def profit(etape1="On vole des caleçons", etape2=partial(print, '...')):
... print(etape1)
... etape2()
...
>>> profit()
On vole des caleçons
...
Utiliser les fonctions derrières les opérations de base
Quand vous utilisez un +
ou un []
, il s’agit ni plus ni moins d’une syntaxe raccourcie pour l’appel d’une fonction. Et Python vous laisse accéder à ces fonctions à travers le module operator
.
>>> for operation in (lambda x: x[0], lambda x: x * 3):
print(operation(ls))
...
a
['a', 3, 'a', 3, 'a', 3]
>>> from operator import itemgetter, mul
>>> for operation in (itemgetter(0), partial(mul, 3)):
print(operation(ls))
...
a
['a', 3, 'a', 3, 'a', 3]
Le module operator
est très riche: opérations logiques (incluant la négation), manipulation de slices, maths de base, set/get d’attributs/de clés/d’index… Il y a de quoi faire.
Mettez à la poubelle map
et filter
Map
permet d’appliquer une fonction a un itérable. Filter
permet de choisir les éléments d’un itérable selon un critère pour former un autre itérable.
>>> nombres = range(10)
>>> paires = filter(lambda x: not x % 2, nombres)
>>> paires
[0, 2, 4, 6, 8]
>>> paires_au_carres = map(lambda x: x * x, paires)
>>> paires_au_carres
[0, 4, 16, 36, 64]
Les listes en intention remplacent avantageusement ces deux fonctions (je crois d’ailleurs qu’elles sont retirées des built-in en Python 3):
>>> [x * x for x in range(10) if not x % 2]
[0, 4, 16, 36, 64]
Je ne suis pas un allergique aux lambdas, et je les utilise assez souvent, mais il est bon de savoir qu’il existe des alternatives.