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:
squares = []
for x in range(10): squares.append(x*x)
- c'est la bonne vieille manière de la syntaxe impérative.
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:
[(lambda x: x*x) for x in range(10)]
Il ne s'agit bien sûr que de 10 copies différentes de la fonction de quadrillage, voir:
>>> [lambda x: x*x for _ in range(3)]
[<function <lambda> at 0x00000000023AD438>, <function <lambda> at 0x00000000023AD4A8>, <function <lambda> at 0x00000000023AD3C8>]
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:
>>> [lambda x: x*x] * 3
[<function <lambda> at 0x00000000023AD2E8>, <function <lambda> at 0x00000000023AD2E8>, <function <lambda> at 0x00000000023AD2E8>]
Voir? 3 fois le même lambda.
Veuillez noter que j'ai utilisé _comme forvariable. Cela n'a rien à voir avec le xdans le lambda(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:
[lambda x: (x*x for x in range(10))]
qui pourrait être:, [[0, 1, 4, ..., 81]]ou [(0, 1, 4, ..., 81)], ou ce que je trouve le plus logique , ce serait un listélément de 1 - un generatorrenvoyant 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 forvariable ET l'utiliser dans vos lambdas ???
Eh bien, alors la merde arrive. Regarde ça:
[lambda x: x * i for i in range(4)]
cela signifie bien sûr:
[(lambda x: x * i) for i in range(4)]
MAIS cela ne veut pas dire:
[(lambda x: x * 0), (lambda x: x * 1), ... (lambda x: x * 3)]
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 iréférence via, et non à sa valeur lors de leur évaluation!
Donc, cette expression:
[(lambda x: x * i) for i in range(4)]
EST à peu près ÉQUIVALENT à:
[(lambda x: x * 3), (lambda x: x * 3), ... (lambda x: x * 3)]
Je suis sûr que nous pourrions en voir plus ici en utilisant un décompilateur python (je veux dire par exemple le dismodule), mais pour une discussion indépendante de Python-VM, cela suffit. Voilà pour la question de l'entretien d'embauche.
Maintenant, comment faire un listmultiplicateur 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 direct ien l'enveloppant dans une autre lambda, qui est appelée dans l'expression de compréhension de liste:
Avant:
>>> a = [(lambda x: x * i) for i in (1, 2)]
>>> a[1](1)
2
>>> a[0](1)
2
Après:
>>> a = [(lambda y: (lambda x: y * x))(i) for i in (1, 2)]
>>> a[1](1)
2
>>> a[0](1)
1
(J'avais également la variable lambda externe = i, mais j'ai décidé que c'était la solution la plus claire - j'ai introduit ypour 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 lambdaet en l'appelant immédiatement avec la valeur actuelle de i. Une autre façon (un peu plus facile à lire - cela ne produit pas d'effet 'WAT') est de stocker la valeur de l' iintérieur d'un partialobjet, et de faire en sorte que l '"intérieur" (original) le lambdaprenne comme argument (passé fourni par l' partialobjet au heure de l'appel), c'est-à-dire:
Après 2:
>>> from functools import partial
>>> a = [partial(lambda y, x: y * x, i) for i in (1, 2)]
>>> a[0](2), a[1](2)
(2, 4)
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:
>>> a = [partial(lambda coef, x: coef * x, coef=i) for i in (1, 2)]
>>> a[0](1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: <lambda>() got multiple values for argument 'coef'
WAT?
>>> a[0]()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: <lambda>() missing 1 required positional argument: 'x'
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à partialde cette façon, cela devient un argument mot-clé, donc il doit venir après l' xargument positionnel , comme ceci:
Après 3:
>>> a = [partial(lambda x, coef: coef * x, coef=i) for i in (1, 2)]
>>> a[0](2), a[1](2)
(2, 4)
Je préférerais la dernière version au lambda imbriqué, mais à chacun leur propre ...
[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.