Diese Frage berührt einen sehr stinkenden Teil der "berühmten" und "offensichtlichen" Python-Syntax - was Vorrang hat, das Lambda oder das For des Listenverständnisses.
Ich glaube nicht, dass der Zweck des OP darin bestand, eine Liste von Quadraten von 0 bis 9 zu erstellen. Wenn dies der Fall wäre, könnten wir noch mehr Lösungen geben:
squares = []
for x in range(10): squares.append(x*x)
- Dies ist die gute alte Art der imperativen Syntax.
Aber es geht nicht darum. Der Punkt ist W (hy) TF. Ist dieser mehrdeutige Ausdruck so kontraintuitiv? Und ich habe am Ende einen idiotischen Fall für Sie, also entlassen Sie meine Antwort nicht zu früh (ich hatte sie in einem Vorstellungsgespräch).
Das Verständnis des OP ergab also eine Liste von Lambdas:
[(lambda x: x*x) for x in range(10)]
Dies ist natürlich nur 10 verschiedene Kopien der Quadratur - Funktion finden Sie :
>>> [lambda x: x*x for _ in range(3)]
[<function <lambda> at 0x00000000023AD438>, <function <lambda> at 0x00000000023AD4A8>, <function <lambda> at 0x00000000023AD3C8>]
Beachten Sie die Speicheradressen der Lambdas - sie sind alle unterschiedlich!
Sie könnten natürlich eine "optimalere" (haha) Version dieses Ausdrucks haben:
>>> [lambda x: x*x] * 3
[<function <lambda> at 0x00000000023AD2E8>, <function <lambda> at 0x00000000023AD2E8>, <function <lambda> at 0x00000000023AD2E8>]
Sehen? 3 mal das gleiche Lambda.
Bitte beachten Sie, dass ich _als forVariable verwendet habe. Es hat nichts mit dem xin der zu tun lambda(es wird lexikalisch überschattet!). Kapiert?
Ich lasse die Diskussion aus, warum die Syntax-Priorität nicht so ist, dass alles bedeutete:
[lambda x: (x*x for x in range(10))]
Das könnte sein: [[0, 1, 4, ..., 81]]oder [(0, 1, 4, ..., 81)], oder was ich am logischsten finde , dies wäre ein list1-Element - eine generatorRückgabe der Werte. Es ist einfach nicht der Fall, die Sprache funktioniert nicht so.
ABER was, wenn ...
Was ist, wenn Sie die forVariable NICHT überschatten UND in Ihrem lambdas verwenden?
Na dann passiert Mist. Schau dir das an:
[lambda x: x * i for i in range(4)]
das heißt natürlich:
[(lambda x: x * i) for i in range(4)]
ABER es bedeutet nicht:
[(lambda x: x * 0), (lambda x: x * 1), ... (lambda x: x * 3)]
Das ist einfach verrückt!
Die Lambdas im Listenverständnis sind ein Abschluss über den Umfang dieses Verständnisses. Ein lexikalischer Abschluss, also beziehen sie sich auf die iVia-Referenz und nicht auf deren Wert, als sie ausgewertet wurden!
Also, dieser Ausdruck:
[(lambda x: x * i) for i in range(4)]
Ist ungefähr gleichbedeutend mit:
[(lambda x: x * 3), (lambda x: x * 3), ... (lambda x: x * 3)]
Ich bin sicher, wir könnten hier mit einem Python-Dekompiler (womit ich zB das disModul meine ) mehr sehen, aber für eine Python-VM-agnostische Diskussion ist dies ausreichend. Soviel zur Frage zum Vorstellungsgespräch.
Wie macht man nun ein listMultiplikator-Lambdas, das sich wirklich mit aufeinanderfolgenden ganzen Zahlen multipliziert? Nun, ähnlich wie bei der akzeptierten Antwort müssen wir die direkte Bindung lösen, iindem wir sie in eine andere einwickeln lambda, die aufgerufen wird im Ausdruck des Listenverständnisses wird :
Vor:
>>> a = [(lambda x: x * i) for i in (1, 2)]
>>> a[1](1)
2
>>> a[0](1)
2
Nach dem:
>>> a = [(lambda y: (lambda x: y * x))(i) for i in (1, 2)]
>>> a[1](1)
2
>>> a[0](1)
1
(Ich hatte auch die äußere Lambda-Variable = i , aber ich entschied, dass dies die klarere Lösung ist - ich habe sie eingeführt, ydamit wir alle sehen können, welche Hexe welche ist).
Bearbeiten 2019-08-30:
Nach einem Vorschlag von @josoler, der auch in einer Antwort von @sheridp enthalten ist - der Wert des Listenverständnisses "Schleifenvariable" kann in ein Objekt "eingebettet" werden - ist der Schlüssel für den Zugriff zum richtigen Zeitpunkt. Der Abschnitt "Nachher" oben tut dies, indem er es in ein anderes einwickelt lambdaund es sofort mit dem aktuellen Wert von aufruft i. Eine andere Möglichkeit (etwas einfacher zu lesen - es wird kein 'WAT'-Effekt erzeugt) besteht darin, den Wert iinnerhalb eines partialObjekts zu speichern und das "innere" (Original) lambdaals Argument zu verwenden (übergeben vonpartial Objekt am übergeben wird) Zeitpunkt des Anrufs), dh:
Nach 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)
Großartig, aber es gibt noch eine kleine Wendung für dich! Nehmen wir an, wir wollen es dem Codeleser nicht leichter machen und den Faktor namentlich übergeben (als Schlüsselwortargument anpartial ). Lassen Sie uns etwas umbenennen:
Nach 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'
Warten Sie ... Wir ändern die Anzahl der Argumente um 1 und wechseln von "zu vielen" zu "zu wenigen"?
Nun, es ist kein echtes WAT, wenn wir coefauf partialdiese Weise übergehen , wird es zu einem Schlüsselwortargument, also muss es nach dem Positionsargument kommen x, wie folgt:
Nach 3:
>>> a = [partial(lambda x, coef: coef * x, coef=i) for i in (1, 2)]
>>> a[0](2), a[1](2)
(2, 4)
Ich würde die letzte Version dem verschachtelten Lambda vorziehen, aber jedem seine eigene ...
[lambda x: x*x for x in range(10)]ist schneller als die erste, da sie keine externe Schleifenfunktion aufruft, f wiederholt.