Pourquoi la sortie des deux compréhensions de liste suivantes est-elle différente, même si f
et la lambda
fonction est la même?
f = lambda x: x*x
[f(x) for x in range(10)]
et
[lambda x: x*x for x in range(10)]
Attention, les deux type(f)
et type(lambda x: x*x)
renvoyez le même type.
[lambda x: x*x for x in range(10)]
est plus rapide que le premier, car il n'appelle pas de fonction de boucle extérieure, f à plusieurs reprises.[x*x for x in range(10)]
c'est mieux.Réponses:
Le premier crée une seule fonction lambda et l'appelle dix fois.
Le second n'appelle pas la fonction. Il crée 10 fonctions lambda différentes. Il met tout cela dans une liste. Pour le rendre équivalent au premier dont vous avez besoin:
Ou mieux encore:
la source
map(lambda x: x*x, range(10))
, ce qui était probablement ce que le PO signifiait en premier lieu.list(map(lambda x: x*x, range(10)))
vous donnera[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
Cette question touche une partie très puante de la syntaxe Python «célèbre» et «évidente» - ce qui prime, le lambda, ou le pour de la compréhension de liste.
Je ne pense pas que le but du PO était de générer une liste de carrés de 0 à 9. Si tel était le cas, nous pourrions donner encore plus de solutions:
Mais ce n'est pas le but. Le point est W (hy) TF est-ce que cette expression ambiguë est si contre-intuitive? Et j'ai un cas idiot pour vous à la fin, alors ne rejetez pas ma réponse trop tôt (je l'ai eue lors d'un entretien d'embauche).
Ainsi, la compréhension de l'OP a renvoyé une liste de lambdas:
Il ne s'agit bien sûr que de 10 copies différentes de la fonction de quadrillage, voir:
Notez les adresses mémoire des lambdas - elles sont toutes différentes!
Vous pourriez bien sûr avoir une version plus "optimale" (haha) de cette expression:
Voir? 3 fois le même lambda.
Veuillez noter que j'ai utilisé
_
commefor
variable. Cela n'a rien à voir avec lex
dans lelambda
(il est éclipsé lexicalement!). Tu piges?Je laisse de côté la discussion, pourquoi la priorité de syntaxe n'est pas ainsi, que tout cela signifiait:
qui pourrait être:,
[[0, 1, 4, ..., 81]]
ou[(0, 1, 4, ..., 81)]
, ou ce que je trouve le plus logique , ce serait unlist
élément de 1 - ungenerator
renvoyant les valeurs. Ce n'est tout simplement pas le cas, la langue ne fonctionne pas de cette façon.MAIS quoi, si ...
Et si vous NE PAS éclipser la
for
variable ET l'utiliser dans voslambda
s ???Eh bien, alors la merde arrive. Regarde ça:
cela signifie bien sûr:
MAIS cela ne veut pas dire:
C'est juste fou!
Les lambdas dans la compréhension de liste sont une clôture sur la portée de cette compréhension. Une fermeture lexicale , donc ils font référence à la
i
référence via, et non à sa valeur lors de leur évaluation!Donc, cette expression:
EST à peu près ÉQUIVALENT à:
Je suis sûr que nous pourrions en voir plus ici en utilisant un décompilateur python (je veux dire par exemple le
dis
module), mais pour une discussion indépendante de Python-VM, cela suffit. Voilà pour la question de l'entretien d'embauche.Maintenant, comment faire un
list
multiplicateur lambdas, qui se multiplie vraiment par des entiers consécutifs? Eh bien, de la même manière que la réponse acceptée, nous devons rompre le lien directi
en l'enveloppant dans une autrelambda
, qui est appelée dans l'expression de compréhension de liste:Avant:
Après:
(J'avais également la variable lambda externe =
i
, mais j'ai décidé que c'était la solution la plus claire - j'ai introduity
pour que nous puissions tous voir quelle sorcière est quelle).Modifier le 30/08/2019:
Suite à une suggestion de @josoler, qui est également présente dans une réponse de @sheridp - la valeur de la "variable de boucle" de compréhension de liste peut être "intégrée" à l'intérieur d'un objet - la clé est d'y accéder au bon moment. La section «Après» ci-dessus le fait en l'enveloppant dans un autre
lambda
et en l'appelant immédiatement avec la valeur actuelle dei
. Une autre façon (un peu plus facile à lire - cela ne produit pas d'effet 'WAT') est de stocker la valeur de l'i
intérieur d'unpartial
objet, et de faire en sorte que l '"intérieur" (original) lelambda
prenne comme argument (passé fourni par l'partial
objet au heure de l'appel), c'est-à-dire:Après 2:
Génial, mais il y a encore un petit rebondissement pour vous! Disons que nous ne voulons pas faciliter la tâche du lecteur de code et transmettre le facteur par son nom (comme argument de mot-clé à
partial
). Faisons un peu de changement de nom:Après 2,5:
WAT?
Attendez ... Nous changeons le nombre d'arguments par 1, et passons de "trop" à "trop peu"?
Eh bien, ce n'est pas un vrai WAT, quand on passe
coef
àpartial
de cette façon, cela devient un argument mot-clé, donc il doit venir après l'x
argument positionnel , comme ceci:Après 3:
Je préférerais la dernière version au lambda imbriqué, mais à chacun leur propre ...
la source
[partial(lambda i, x: i * x, i) for i in (1, 2)]
La grande différence est que le premier exemple appelle en fait le lambda
f(x)
, contrairement au deuxième exemple.Votre premier exemple est équivalent à
[(lambda x: x*x)(x) for x in range(10)]
tandis que votre deuxième exemple est équivalent à[f for x in range(10)]
.la source
Le premier
s'exécute
f()
pour chaque valeur de la plage, il le faitf(x)
pour chaque valeurle deuxième
exécute le lambda pour chaque valeur de la liste, donc il génère toutes ces fonctions.
la source
Les gens ont donné de bonnes réponses mais ont oublié de mentionner la partie la plus importante à mon avis: dans le deuxième exemple, la
X
compréhension de la liste n'est PAS la même que celleX
de lalambda
fonction, elles sont totalement indépendantes. Donc, le deuxième exemple est en fait le même que:Les itérations internes sur
range(10)
ne sont responsables que de la création de 10 fonctions lambda similaires dans une liste (10 fonctions distinctes mais totalement similaires - renvoyant la puissance 2 de chaque entrée).D'autre part, le premier exemple fonctionne totalement différemment, car le X des itérations DO interagit avec les résultats, pour chaque itération la valeur est
X*X
donc le résultat serait[0,1,4,9,16,25, 36, 49, 64 ,81]
la source
Les autres réponses sont correctes, mais si vous essayez de faire une liste de fonctions, chacune avec un paramètre différent, qui peuvent être exécutées plus tard , le code suivant le fera:
Bien que l'exemple soit artificiel, je l'ai trouvé utile lorsque je voulais une liste de fonctions qui impriment chacune quelque chose de différent, c'est-à-dire
la source