Frage Python: Lambda-Funktion in Listenbeschreibungen


Warum unterscheidet sich die Ausgabe der folgenden beiden Listenkomprehensionen, 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, sowohl Typ (f) als auch Typ (Lambda x: x * x) geben den gleichen Typ zurück.


75
2018-05-20 18:39


Ursprung


Antworten:


Der erste erstellt eine einzelne Lambda-Funktion und ruft sie zehn Mal auf.

Der zweite ruft die Funktion nicht auf. Es erzeugt 10 verschiedene Lambda-Funktionen. Es fügt alle diese in eine Liste ein. Um es gleich zu machen, benötigen Sie:

[(lambda x: x*x)(x) for x in range(10)]

Oder noch besser:

[x*x for x in range(10)]

153
2018-05-20 18:41



Diese Frage berührt einen sehr stinkenden Teil der "berühmten" und "offensichtlichen" Python-Syntax - was Vorrang hat, das Lambda oder das For of List-Verständnis.

Ich glaube nicht, dass der Zweck des OP darin bestand, eine Liste von Quadraten von 0 bis 9 zu erstellen. Wenn das der Fall wäre, könnten wir noch mehr Lösungen geben:

squares = []
for x in range(10): squares.append(x*x)
  • das ist die gute Art der imperativen Syntax.

Aber das ist nicht der Punkt. Der Punkt ist W (hy) TF ist dieser zweideutige Ausdruck so kontraintuitiv? Und am Ende habe ich einen idiotischen Fall für dich, also verwerfe meine Antwort nicht zu früh (ich hatte sie in einem Vorstellungsgespräch).

Also, das Verständnis des OP gab eine Liste von Lambdas zurück:

[(lambda x: x*x) for x in range(10)]

Das ist natürlich nur 10 anders Kopien der Quadrierfunktion, siehe:

>>> [lambda x: x*x for _ in range(3)]
[<function <lambda> at 0x00000000023AD438>, <function <lambda> at 0x00000000023AD4A8>, <function <lambda> at 0x00000000023AD3C8>]

Hinweis die Speicheradressen der Lambdas - sie sind alle verschieden!

Sie könnten natürlich eine "optimale" (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 gebraucht habe _ als die for Variable. Es hat nichts mit dem zu tun x in dem lambda (Es ist lexikalisch überschattet!). Hole es?

Ich verlasse die Diskussion, warum die Syntaxpräzedenz nicht so ist, dass es alles bedeutete:

[lambda x: (x*x for x in range(10))]

was könnte sein: [[0, 1, 4, ..., 81]], oder [(0, 1, 4, ..., 81)], oder was ich am logischsten finde, das wäre ein list von 1 Element - a generator Rückgabe der Werte. Es ist einfach nicht der Fall, die Sprache funktioniert nicht so.

ABER Was wäre wenn...

Was, wenn du das nicht überschattest? for Variable, UND verwenden Sie es in Ihrem lambdas ???

Nun, dann passiert Scheiße. 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 eine Schlussfolgerung über den Umfang dieses Verständnisses. EIN lexikalisch Schließung, so beziehen sie sich auf die i über Referenz, und nicht ihren Wert, wenn sie ausgewertet wurden!

Also, dieser Ausdruck:

[(lambda x: x * i) for i in range(4)]

IS ist ungefähr äquivalent zu:

[(lambda x: x * 3), (lambda x: x * 3), ... (lambda x: x * 3)]

Ich bin mir sicher, dass wir hier mehr mit einem Python - Decompiler sehen könnten (mit dem ich z. B. den dis Modul), aber für Python-VM-agnostische Diskussion ist das genug. So viel zu der Vorstellungsfrage.

Nun, wie man a macht list von Multiplikator-Lambdas, die sich wirklich mit aufeinanderfolgenden ganzen Zahlen multiplizieren? Nun, ähnlich wie die akzeptierte Antwort, müssen wir die direkte Verbindung zu knacken i indem man es in ein anderes einwickelt lambda, die aufgerufen wird Innerhalb der Listenverständnisausdruck:

Vor:

>>> a = [(lambda x: x * i) for i in (1, 2)]
>>> a[1](1)
2
>>> a[0](1)
2

Nach:

>>> a = [(lambda y: (lambda x: y * x))(i) for i in (1, 2)]
>>> a[1](1)
2
>>> a[0](1)
1

(Ich hatte die äußere Lambda-Variable auch = i, aber ich entschied, dass das die klarere Lösung ist - ich habe eingeführt y damit wir alle sehen können welche Hexe was ist).


47
2017-12-01 13:14



Der große Unterschied ist, dass das erste Beispiel das Lambda aufruft f(x), während das zweite Beispiel nicht.

Ihr erstes Beispiel entspricht dem [(lambda x: x*x)(x) for x in range(10)] während Ihr zweites Beispiel entspricht [f for x in range(10)].


17
2018-05-20 18:41



Der erste

f = lambda x: x*x
[f(x) for x in range(10)]

läuft f() für jeden Wert in dem Bereich, so tut es f(x) für jeden Wert

der zweite

[lambda x: x*x for x in range(10)]

Läuft das Lambda für jeden Wert in der Liste, so generiert es alle diese Funktionen.


7
2018-05-20 18:42



Die Leute gaben gute Antworten, vergaßen aber, den wichtigsten Teil meiner Meinung nach zu erwähnen: Im zweiten Beispiel X der Liste Verständnis ist nicht das gleiche wie die X des lambda Funktion, sie sind völlig unabhängig. Das zweite Beispiel ist also dasselbe wie:

[Lambda X: X*X for I in range(10)]

Die internen Iterationen auf range(10) sind nur verantwortlich für das Erstellen von 10 ähnlichen Lambda-Funktionen in einer Liste (10 separate Funktionen, aber völlig ähnlich - die Leistung 2 jedes Eingangs zurückgeben).

Auf der anderen Seite arbeitet das erste Beispiel völlig anders, weil das X der Iterationen mit den Ergebnissen interagiert, für jede Iteration der Wert X*X so wäre das Ergebnis [0,1,4,9,16,25, 36, 49, 64 ,81]


2
2017-08-27 15:46



Die anderen Antworten sind korrekt, aber wenn Sie versuchen, eine Liste von Funktionen mit jeweils einem anderen Parameter zu erstellen, die ausgeführt werden können später, der folgende Code wird das tun:

import functools
a = [functools.partial(lambda x: x*x, x) for x in range(10)]

b = []
for i in a:
    b.append(i())

In [26]: b
Out[26]: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Während das Beispiel erfunden ist, fand ich es nützlich, wenn ich eine Liste von Funktionen wollte, die jeweils etwas anderes drucken, d.h.

import functools
a = [functools.partial(lambda x: print(x), x) for x in range(10)]

for i in a:
    i()

2
2018-01-24 14:52