Warum ist die Ausgabe der folgenden beiden Listenverständnisse unterschiedlich, obwohl f
und die lambda
Funktion gleich sind?
f = lambda x: x*x
[f(x) for x in range(10)]
und
[lambda x: x*x for x in range(10)]
Wohlgemerkt, beide type(f)
und type(lambda x: x*x)
geben den gleichen Typ zurück.
[lambda x: x*x for x in range(10)]
ist schneller als die erste, da sie keine externe Schleifenfunktion aufruft, f wiederholt.[x*x for x in range(10)]
ist es besser.Antworten:
Der erste erstellt eine einzelne Lambda-Funktion und ruft sie zehnmal auf.
Der zweite ruft die Funktion nicht auf. Es werden 10 verschiedene Lambda-Funktionen erstellt. Es bringt all diese in eine Liste. Um es dem ersten gleichzusetzen, benötigen Sie:
Oder noch besser:
quelle
map(lambda x: x*x, range(10))
was das OP wahrscheinlich überhaupt bedeutete.list(map(lambda x: x*x, range(10)))
wird Ihnen geben[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
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:
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:
Dies ist natürlich nur 10 verschiedene Kopien der Quadratur - Funktion finden Sie :
Beachten Sie die Speicheradressen der Lambdas - sie sind alle unterschiedlich!
Sie könnten natürlich eine "optimalere" (haha) Version dieses Ausdrucks haben:
Sehen? 3 mal das gleiche Lambda.
Bitte beachten Sie, dass ich
_
alsfor
Variable verwendet habe. Es hat nichts mit demx
in der zu tunlambda
(es wird lexikalisch überschattet!). Kapiert?Ich lasse die Diskussion aus, warum die Syntax-Priorität nicht so ist, dass alles bedeutete:
Das könnte sein:
[[0, 1, 4, ..., 81]]
oder[(0, 1, 4, ..., 81)]
, oder was ich am logischsten finde , dies wäre einlist
1-Element - einegenerator
Rückgabe der Werte. Es ist einfach nicht der Fall, die Sprache funktioniert nicht so.ABER was, wenn ...
Was ist, wenn Sie die
for
Variable NICHT überschatten UND in Ihremlambda
s verwenden?Na dann passiert Mist. Schau dir das an:
das heißt natürlich:
ABER es bedeutet nicht:
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
i
Via-Referenz und nicht auf deren Wert, als sie ausgewertet wurden!Also, dieser Ausdruck:
Ist ungefähr gleichbedeutend mit:
Ich bin sicher, wir könnten hier mit einem Python-Dekompiler (womit ich zB das
dis
Modul 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
list
Multiplikator-Lambdas, das sich wirklich mit aufeinanderfolgenden ganzen Zahlen multipliziert? Nun, ähnlich wie bei der akzeptierten Antwort müssen wir die direkte Bindung lösen,i
indem wir sie in eine andere einwickelnlambda
, die aufgerufen wird im Ausdruck des Listenverständnisses wird :Vor:
Nach dem:
(Ich hatte auch die äußere Lambda-Variable =
i
, aber ich entschied, dass dies die klarere Lösung ist - ich habe sie eingeführt,y
damit 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
lambda
und es sofort mit dem aktuellen Wert von aufrufti
. Eine andere Möglichkeit (etwas einfacher zu lesen - es wird kein 'WAT'-Effekt erzeugt) besteht darin, den Werti
innerhalb einespartial
Objekts zu speichern und das "innere" (Original)lambda
als Argument zu verwenden (übergeben vonpartial
Objekt am übergeben wird) Zeitpunkt des Anrufs), dh:Nach 2:
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 an
partial
). Lassen Sie uns etwas umbenennen:Nach 2,5:
WAT?
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
coef
aufpartial
diese Weise übergehen , wird es zu einem Schlüsselwortargument, also muss es nach dem Positionsargument kommenx
, wie folgt:Nach 3:
Ich würde die letzte Version dem verschachtelten Lambda vorziehen, aber jedem seine eigene ...
quelle
[partial(lambda i, x: i * x, i) for i in (1, 2)]
Der große Unterschied besteht darin, dass das erste Beispiel tatsächlich das Lambda aufruft
f(x)
, während das zweite Beispiel dies nicht tut.Ihr erstes Beispiel entspricht,
[(lambda x: x*x)(x) for x in range(10)]
während Ihr zweites Beispiel entspricht[f for x in range(10)]
.quelle
Der erste
wird
f()
für jeden Wert im Bereich ausgeführtf(x)
für jeden Wertder zweite
führt das Lambda für jeden Wert in der Liste aus, sodass alle diese Funktionen generiert werden.
quelle
Die Leute gaben gute Antworten, vergaßen aber, den wichtigsten Teil meiner Meinung nach zu erwähnen: Im zweiten Beispiel ist
X
das Listenverständnis NICHT das gleiche wie dasX
derlambda
Funktion, sie sind völlig unabhängig. Das zweite Beispiel ist also dasselbe wie:Die internen Iterationen auf
range(10)
sind nur für die Erstellung von 10 ähnlichen Lambda-Funktionen in einer Liste verantwortlich (10 separate Funktionen, aber völlig ähnlich - Rückgabe der Potenz 2 jedes Eingangs).Andererseits funktioniert das erste Beispiel völlig anders, da das X der Iterationen mit den Ergebnissen interagiert. Für jede Iteration ist der Wert
X*X
so, dass das Ergebnis wäre[0,1,4,9,16,25, 36, 49, 64 ,81]
quelle
Die anderen Antworten sind korrekt. Wenn Sie jedoch versuchen, eine Liste von Funktionen mit jeweils unterschiedlichen Parametern zu erstellen, die später ausgeführt werden können , führt der folgende Code Folgendes aus:
Während das Beispiel erfunden ist, fand ich es nützlich, wenn ich eine Liste von Funktionen haben wollte, die jeweils etwas anderes drucken, dh
quelle