J'utilise principalement des fonctions lambda mais parfois des fonctions imbriquées qui semblent fournir le même comportement.
Voici quelques exemples triviaux où ils font fonctionnellement la même chose si l'un ou l'autre était trouvé dans une autre fonction:
Fonction lambda
>>> a = lambda x : 1 + x
>>> a(5)
6
Fonction imbriquée
>>> def b(x): return 1 + x
>>> b(5)
6
Y a-t-il des avantages à utiliser l'un par rapport à l'autre? (Performance? Lisibilité? Limitations? Cohérence? Etc.)
Est-ce même important? Si ce n'est pas le cas, cela viole-t-il le principe Pythonic:
“Il devrait y avoir un - et de préférence un seul - moyen évident de le faire” .
Si vous devez attribuer un nom à lambda
, utilisez plutôt un def
. def
s sont simplement du sucre syntaxique pour une assignation, donc le résultat est le même, et ils sont beaucoup plus souples et lisibles.
lambda
s peut être utilisé pour utiliser une fois, jette des fonctions qui n'auront pas de nom.
Cependant, ce cas d'utilisation est très rare. Il est rarement nécessaire de faire passer des objets fonction non nommés.
Les fonctions intégrées map()
et filter()
ont besoin d'objets fonction, mais la liste des compréhensions et les expressions du générateur sont généralement plus lisibles que ces fonctions et peuvent couvrir tous les cas d'utilisation, sans nécessiter de lambda.
Pour les cas où vous avez vraiment besoin d'un petit objet fonction, vous devriez utiliser les fonctions du module operator
, comme operator.add
au lieu de lambda x, y: x + y
.
Si vous avez encore besoin d'une variable lambda
non couverte, vous pouvez envisager d'écrire une variable def
, juste pour être plus lisible. Si la fonction est plus complexe que celle du module operator
, une def
est probablement meilleure.
Ainsi, dans le monde réel, les cas d'utilisation lambda
sont très rares.
En pratique, il y a deux différences pour moi:
Le premier concerne ce qu’ils font et ce qu’ils retournent:
def est un mot clé qui ne renvoie rien et crée un "nom" dans l'espace de noms local.
lambda est un mot clé qui renvoie un objet fonction et ne crée pas de "nom" dans l'espace de noms local.
Par conséquent, si vous avez besoin d'appeler une fonction qui prend un objet fonction, la seule façon de le faire dans une ligne de code python est d'utiliser un lambda. Il n'y a pas d'équivalent avec def.
Dans certains cadres, cela est en fait assez courant; par exemple, j'utilise Twisted beaucoup, et donc faire quelque chose comme
d.addCallback(lambda result: setattr(self, _someVariable, result))
est assez courant et plus concis avec les lambdas.
La deuxième différence concerne ce que la fonction réelle est autorisée à faire.
Par exemple,
def p(x): print x
fonctionne comme prévu, tandis que
lambda x: print x
est une SyntaxError.
Bien sûr, il existe des solutions de contournement - remplacez print
par sys.stdout.write
ou import
par __import__
. Mais généralement, il vaut mieux utiliser une fonction dans ce cas.
Dans cette interview, / Guido van Rossum dit qu'il souhaiterait ne pas avoir laissé "lambda" dans Python:
"Q. Quelle caractéristique de Python vous satisfait le moins?
Parfois, j'ai été trop rapide dans l'acceptation de contributions et je me suis rendu compte plus tard que c'était une erreur. Certaines fonctionnalités de programmation, telles que les fonctions lambda, en sont un exemple. lambda est un mot clé qui vous permet de créer une petite fonction anonyme. Les fonctions intégrées telles que mapper, filtrer et réduire exécutent une fonction sur un type de séquence, tel qu'une liste.
En pratique, cela ne s'est pas bien passé. Python n'a que deux portées: locale et globale. Cela rend l’écriture des fonctions lambda pénible, car vous voulez souvent accéder aux variables de l’étendue où lambda a été défini, mais vous ne pouvez pas en raison des deux étendues. Il y a un moyen de contourner cela, mais c'est une sorte de kludge. Il semble souvent beaucoup plus facile en Python d’utiliser une boucle for au lieu de jouer avec les fonctions lambda. map and friends ne fonctionnent bien que s’il existe déjà une fonction intégrée qui fait ce que vous voulez.
IMHO, Iambdas peut être pratique parfois, mais sont généralement pratiques au détriment de la lisibilité. Pouvez-vous me dire ce que cela fait:
str(reduce(lambda x,y:x+y,map(lambda x:x**x,range(1,1001))))[-10:]
Je l'ai écrit et il m'a fallu une minute pour le comprendre. Ceci est du projet Euler - je ne dirai pas quel problème parce que je déteste les spoilers, mais il fonctionne en 0.124 secondes :)
Pour n = 1000, voici le temps d'appeler une fonction vs un lambda:
In [11]: def f(a, b):
return a * b
In [12]: g = lambda x, y: x * y
In [13]: %%timeit -n 100
for a in xrange(n):
for b in xrange(n):
f(a, b)
....:
100 loops, best of 3: 285 ms per loop
In [14]: %%timeit -n 100
for a in xrange(n):
for b in xrange(n):
g(a, b)
....:
100 loops, best of 3: 298 ms per loop
In [15]: %%timeit -n 100
for a in xrange(n):
for b in xrange(n):
(lambda x, y: x * y)(a, b)
....:
100 loops, best of 3: 462 ms per loop
Je suis d'accord avec le conseil de nosklo: si vous devez donner un nom à la fonction, utilisez def
. Je réserve des fonctions lambda
pour les cas où je ne fais que passer un court extrait de code à une autre fonction, par exemple:
a = [ (1,2), (3,4), (5,6) ]
b = map( lambda x: x[0]+x[1], a )
Performance:
Créer une fonction avec lambda
est légèrement plus rapide que de le créer avec def
. La différence est due à la création d'une entrée de nom dans la table des sections locales par def
. La fonction résultante a la même vitesse d'exécution.
Lisibilité:
Les fonctions Lambda sont un peu moins lisibles pour la plupart des utilisateurs de Python, mais aussi beaucoup plus concises dans certaines circonstances. Envisagez de passer d'une routine non fonctionnelle à fonctionnelle:
# Using non-functional version.
heading(math.sqrt(v.x * v.x + v.y * v.y), math.atan(v.y / v.x))
# Using lambda with functional version.
fheading(v, lambda v: math.sqrt(v.x * v.x + v.y * v.y), lambda v: math.atan(v.y / v.x))
# Using def with functional version.
def size(v):
return math.sqrt(v.x * v.x + v.y * v.y)
def direction(v):
return math.atan(v.y / v.x)
deal_with_headings(v, size, direction)
Comme vous pouvez le constater, la version lambda
est plus courte et "simple" en ce sens qu'il suffit d'ajouter lambda v:
à la version d'origine non fonctionnelle pour la convertir en version fonctionnelle. C'est aussi beaucoup plus concis. Mais souvenez-vous que beaucoup d'utilisateurs de Python seront déroutés par la syntaxe lambda. Ainsi, ce que vous perdez en longueur et en complexité peut être récupéré dans la confusion par d'autres codeurs.
Limites:
lambda
ne peuvent être utilisées qu'une seule fois, sauf si elles sont affectées à un nom de variable.lambda
affectées à des noms de variables n’ont aucun avantage sur les fonctions def
.lambda
peuvent être difficiles, voire impossibles à maîtriser.def
doivent être choisis avec soin pour être raisonnablement descriptifs et uniques ou du moins non utilisés dans leur portée.Cohérence:
Python évite généralement les conventions de programmation fonctionnelle en faveur de la sémantique procédurale et de la sémantique objective. L'opérateur lambda
contraste directement avec ce biais. De plus, la fonction def
ajoute de la diversité à votre syntaxe, au lieu de lambda
déjà répandu. Certains considéreraient cela moins cohérent.
Fonctions préexistantes:
Comme l'ont noté d'autres personnes, de nombreuses utilisations de lambda
sur le terrain peuvent être remplacées par des membres de operator
ou d'autres modules. Par exemple:
do_something(x, y, lambda x, y: x + y)
do_something(x, y, operator.add)
L'utilisation de la fonction préexistante peut rendre le code plus lisible dans de nombreux cas.
Le principe pythonique: "Il devrait y avoir un moyen, et de préférence un seul, évident de le faire."
Cela ressemble à la doctrine de la { source unique de vérité }. Malheureusement, le principe de la seule façon évidente de le faire a toujours été davantage une aspiration nostalgique à Python qu’un véritable principe directeur. Considérez les très puissantes connaissances sur les tableaux en Python. Elles sont fonctionnellement équivalentes aux fonctions map
et filter
:
[e for e in some_array if some_condition(e)]
filter(some_array, some_condition)
lambda
et def
sont les mêmes.
C'est une question d'opinion, mais je dirais que tout ce qui, dans le langage Python, est destiné à un usage général et qui ne casse manifestement rien, est suffisamment "Pythonic".
Tout en étant d’accord avec les autres réponses, il est parfois plus lisible. Voici un exemple où lambda
est pratique, dans un cas d'utilisation, je rencontre toujours une dimension N defaultdict
.
Voici un exemple:
from collections import defaultdict
d = defaultdict(lambda: defaultdict(list))
d['Foo']['Bar'].append(something)
Je le trouve plus lisible que de créer une def
pour la deuxième dimension. Ceci est encore plus significatif pour les dimensions supérieures.
Lambda a toujours été utilisé principalement pour les fonctions de rappel simples et pour les fonctions map, réduire, filtrer, qui nécessitent une fonction en tant qu'argument. Avec les compréhensions de liste devenant la norme, et les ajouts autorisés si comme dans:
x = [f for f in range(1, 40) if f % 2]
il est difficile d'imaginer un cas réel d'utilisation quotidienne de lambda. En conséquence, je dirais, évitez lambda et créez des fonctions imbriquées.
Une limitation importante des lambdas est qu'ils ne peuvent contenir rien d'autre qu'une expression. Il est presque impossible pour une expression lambda de produire autre chose que des effets secondaires triviaux, car elle ne peut avoir un corps aussi riche que la fonction def
'ed.
Cela étant dit, Lua a influencé mon style de programmation en vue d’une utilisation intensive des fonctions anonymes, et j’en ai jeté mon code. En plus de cela, j'ai tendance à penser à map/réduire en tant qu'opérateurs abstraits d'une manière que je ne considère pas comme des compréhensions de liste ou des générateurs, presque comme si je reportais explicitement une décision d'implémentation en utilisant ces opérateurs.
Edit: Ceci est une question assez ancienne, et mes opinions sur la question ont changé, un peu.
Tout d’abord, je suis fortement contre l’attribution d’une expression lambda
à une variable; comme python a une syntaxe spéciale juste pour ça (indice, def
). En plus de cela, de nombreuses utilisations de lambda, même lorsqu'elles ne reçoivent pas de nom, ont des implémentations prédéfinies (et plus efficaces). Par exemple, l'exemple en question peut être abrégé en (1).__add__
, sans qu'il soit nécessaire de l'envelopper dans une variable lambda
ou def
. De nombreuses autres utilisations courantes peuvent être satisfaites avec une combinaison des modules operator
, itertools
et functools
.
Une utilisation pour lambdas que j'ai trouvée ... est dans les messages de débogage.
Puisque lambdas peut être évalué paresseusement, vous pouvez avoir un code comme celui-ci:
log.debug(lambda: "this is my message: %r" % (some_data,))
au lieu de éventuellement cher:
log.debug("this is my message: %r" % (some_data,))
qui traite la chaîne de format même si l'appel de débogage ne produit pas de sortie en raison du niveau de journalisation actuel.
Bien entendu, pour fonctionner correctement, le module de journalisation utilisé doit prendre en charge lambdas en tant que "paramètres paresseux" (comme le fait mon module de journalisation).
La même idée peut être appliquée à tout autre cas d'évaluation paresseuse pour la création de valeur de contenu à la demande.
Par exemple, cet opérateur ternaire personnalisé:
def mif(condition, when_true, when_false):
if condition:
return when_true()
else:
return when_false()
mif(a < b, lambda: a + a, lambda: b + b)
au lieu de:
def mif(condition, when_true, when_false):
if condition:
return when_true
else:
return when_false
mif(a < b, a + a, b + b)
avec lambdas, seule l'expression sélectionnée par la condition sera évaluée, sans lambdas, les deux seront évalués.
Bien sûr, vous pouvez simplement utiliser des fonctions à la place de lambdas, mais pour les expressions courtes, les mots lambdas sont (c) plus minces.
Plus préférables: fonctions lambda ou fonctions imbriquées (
def
)?
Il y a un avantage à utiliser un lambda par rapport à une fonction régulière (ils sont créés dans une expression) et plusieurs inconvénients. Pour cette raison, je préfère créer des fonctions avec le mot clé def
plutôt qu'avec lambdas.
Un lambda donne le même type d'objet qu'une fonction normale
>>> l = lambda: 0
>>> type(l)
<class 'function'>
>>> def foo(): return 0
...
>>> type(foo)
<class 'function'>
>>> type(foo) is type(l)
True
Puisque les lambdas sont des fonctions, ce sont des objets de première classe.
Lambdas et fonctions:
Mais les lambdas manquent, par défaut, de choses que les fonctions obtiennent via la syntaxe de définition complète.
__name__
d'un lamba est '<lambda>'
Les Lambda sont des fonctions anonymes, après tout, donc ils ne connaissent pas leur propre nom.
>>> l.__name__
'<lambda>'
>>> foo.__name__
'foo'
Ainsi, les lambda ne peuvent pas être recherchés par programme dans leur espace de noms.
Cela limite certaines choses. Par exemple, foo
peut être recherché avec un code sérialisé, alors que l
ne peut pas:
>>> import pickle
>>> pickle.loads(pickle.dumps(l))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
_pickle.PicklingError: Can't pickle <function <lambda> at 0x7fbbc0464e18>:
attribute lookup <lambda> on __main__ failed
Nous pouvons rechercher foo
parfaitement - car il connaît son propre nom:
>>> pickle.loads(pickle.dumps(foo))
<function foo at 0x7fbbbee79268>
Fondamentalement, les lambdas ne sont pas documentés. Réécrivons foo
pour être mieux documenté:
def foo() -> int:
"""a nullary function, returns 0 every time"""
return 0
Maintenant, foo a de la documentation:
>>> foo.__annotations__
{'return': <class 'int'>}
>>> help(foo)
Help on function foo in module __main__:
foo() -> int
a nullary function, returns 0 every time
Considérant que nous n’avons pas le même mécanisme pour donner les mêmes informations aux lambdas:
>>> help(l)
Help on function <lambda> in module __main__:
<lambda> lambda (...)
Mais on peut les pirater:
>>> l.__doc__ = 'nullary -> 0'
>>> l.__annotations__ = {'return': int}
>>> help(l)
Help on function <lambda> in module __main__:
<lambda> lambda ) -> in
nullary -> 0
Mais il y a probablement une erreur qui perturbe la sortie de l'aide, cependant.
Lambdas ne peut pas renvoyer des instructions complexes, mais uniquement des expressions.
>>> lambda: if True: 0
File "<stdin>", line 1
lambda: if True: 0
^
SyntaxError: invalid syntax
Les expressions peuvent certes être assez complexes, et si vous essayez très difficile, vous pourrez probablement accomplir la même chose avec un lambda, mais la complexité ajoutée nuit davantage à la rédaction de code clair.
Nous utilisons Python pour la clarté et la maintenabilité. La surutilisation de lambdas peut jouer contre cela.
C'est le seul avantage possible. Comme vous pouvez créer un lambda avec une expression, vous pouvez le créer dans un appel de fonction.
La création d'une fonction dans un appel de fonction évite la recherche de noms (peu coûteuse) par rapport à celle créée ailleurs.
Cependant, étant donné que Python est strictement évalué, il n’ya aucun autre avantage en termes de performances à éviter d’éviter la recherche de nom.
Pour une expression très simple, je pourrais choisir un lambda.
J'ai aussi tendance à utiliser lambdas lors de l'utilisation de Python interactif, pour éviter les lignes multiples quand on le fait. J'utilise le type de format de code suivant lorsque je souhaite transmettre un argument à un constructeur lors de l'appel de timeit.repeat
:
import timeit
def return_nullary_lambda(return_value=0):
return lambda: return_value
def return_nullary_function(return_value=0):
def nullary_fn():
return return_value
return nullary_fn
Et maintenant:
>>> min(timeit.repeat(lambda: return_nullary_lambda(1)))
0.24312214995734394
>>> min(timeit.repeat(lambda: return_nullary_function(1)))
0.24894469301216304
Je crois que la légère différence de temps ci-dessus peut être attribuée à la recherche de nom dans return_nullary_function
- notez que c'est très négligeable.
Les lambda conviennent aux situations informelles dans lesquelles vous souhaitez minimiser les lignes de code au lieu de faire un point singulier.
Les lambda sont mauvaises pour les situations plus formelles où vous avez besoin de clarté pour les éditeurs de code qui viendront plus tard, en particulier dans les cas où ils ne sont pas triviaux.
Nous savons que nous sommes supposés donner à nos objets de bons noms. Comment pouvons-nous le faire lorsque l'objet a no name?
Pour toutes ces raisons, je préfère créer des fonctions avec def
au lieu de lambda
.
Si vous souhaitez simplement affecter le lambda à une variable de la portée locale, vous pouvez également utiliser le paramètre def, car il est plus lisible et peut être développé plus facilement à l'avenir:
fun = lambda a, b: a ** b # a pointless use of lambda
map(fun, someList)
ou
def fun(a, b): return a ** b # more readable
map(fun, someList)
Considérant un exemple simple,
# CREATE ONE FUNCTION AND USE IT TO PERFORM MANY OPERATIONS ON SAME TYPE OF DATA STRUCTURE.
def variousUse(a,b=lambda x:x[0]):
return [b(i) for i in a]
dummyList = [(0,1,2,3),(4,5,6,7),(78,45,23,43)]
variousUse(dummyList) # extract first element
variousUse(dummyList,lambda x:[x[0],x[2],x[3]]) # extract specific indexed element
variousUse(dummyList,lambda x:x[0]+x[2]) # add specific elements
variousUse(dummyList,lambda x:x[0]*x[2]) # multiply specific elements
Je suis d'accord avec nosklo. En passant, même avec un utilisez une fois, jetez fonction, la plupart du temps, vous voulez simplement utiliser quelque chose du module opérateur.
PAR EXEMPLE :
Vous avez une fonction avec cette signature: myFunction (data, callback function).
Vous voulez passer une fonction qui ajoute 2 éléments.
Utiliser lambda:
myFunction(data, (lambda x, y : x + y))
La façon pythonique:
import operator
myFunction(data, operator.add)
Bien entendu, il s’agit d’un exemple simple, mais le module opérateur contient de nombreux éléments, notamment les paramètres de réglage et d’obtention des éléments pour list et dict. Vraiment cool.