Ist es möglich, mehrere Anweisungen in einem Python-Lambda-Ausdruck zu haben?

112

Ich bin ein Python-Neuling, der versucht, Folgendes zu erreichen:

Ich habe eine Liste von Listen:

lst = [[567,345,234],[253,465,756, 2345],[333,777,111, 555]]

Ich möchte die Karte zuerst in eine andere Liste einfügen, die nur die zweitkleinste Zahl aus jeder Unterliste enthält. Das Ergebnis sollte also sein:

[345, 465, 333]

Wenn ich zum Beispiel nur an der kleinsten Zahl interessiert wäre, könnte ich Folgendes tun:

map(lambda x: min(x),lst)

Ich wünschte, ich könnte das tun:

map(lambda x: sort(x)[1],lst)

aber sortieren verkettet nicht. (gibt None zurück)

so etwas ist auch nicht erlaubt:

map(lambda x: sort(x); x[1],lst) #hence the multiple statement question

Gibt es eine Möglichkeit, dies mit Map in Python zu tun, ohne eine benannte Funktion zu definieren ? (Es ist zum Beispiel einfach mit anonymen Blöcken in Ruby)

ottodidakt
quelle
ZEE
1
Sie können keine Anweisungen ausführen, aber Sie können Funktionen in der Lambda-Funktion aufrufen, sodass der unpythonische Dirty-Hack lambda x: sort(x) OR x[1]funktionieren würde: Hier wertet der OR sein erstes Argument (Rückgabewert None) als Bool (=> False) und in diesem Fall als OR aus gibt sein zweites Argument zurück. Aber wie die Antworten sagen, vermeiden Sie besser Lambda.
Max

Antworten:

136

Ich kann hier verschiedene Antworten geben, von Ihrer spezifischen Frage bis hin zu allgemeineren Anliegen. Also vom spezifischsten zum allgemeinsten:

Frage: Können Sie einem Lambda mehrere Aussagen hinzufügen?

A. Nein. Aber Sie brauchen eigentlich kein Lambda. Sie können die Anweisungen defstattdessen in ein setzen . dh:

def second_lowest(l):
    l.sort()
    return l[1]

map(second_lowest, lst)

Frage: Können Sie das zweitniedrigste Element aus einem Lambda erhalten, indem Sie die Liste sortieren?

A. Ja. Wie die Antwort von alex zeigt, sorted()handelt es sich um eine Sortierversion, die eine neue Liste erstellt, anstatt sie direkt zu sortieren, und die verkettet werden kann. Beachten Sie, dass dies wahrscheinlich das ist, was Sie verwenden sollten - es ist eine schlechte Praxis, wenn Ihre Karte Nebenwirkungen auf die ursprüngliche Liste hat.

Frage: Wie soll ich aus jeder Liste in einer Folge von Listen das zweitniedrigste Element erhalten?

A. sorted(l)[1] ist eigentlich nicht der beste Weg dafür. Es hat eine O (N log (N)) -Komplexität, während eine O (n) -Lösung existiert. Dies finden Sie im Heapq-Modul.

>>> import  heapq
>>> l = [5,2,6,8,3,5]
>>> heapq.nsmallest(l, 2)
[2, 3]

Also benutze einfach:

map(lambda x: heapq.nsmallest(x,2)[1],  list_of_lists)

Es wird normalerweise auch als klarer angesehen, ein Listenverständnis zu verwenden, das das Lambda insgesamt vermeidet:

[heapq.nsmallest(x,2)[1] for x in list_of_lists]
Brian
quelle
3
Ich glaube nicht, dass Sie Recht haben, wenn die O (n) -Lösung im heapq-Modul gefunden wird. Alles, was Sie dort tun, ist das Heap-Sortieren der Liste, die O (n log n) ist, und das Auffinden der kleinsten Elemente.
avpx
8
Die Dokumentation ( docs.python.org/2/library/heapq.html#heapq.nsmallest ) warnt tatsächlich davor, dass die Verwendung von heapq.nsmallest () möglicherweise weniger effizient ist als die Verwendung von sorted (), sodass nur Messungen erkennen können, um welche Lösung es sich handelt am schnellsten in Ihrem Fall. heapq.nsmallest () hat jedoch eine Komplexität von O (k * log (n) + n). Ich denke, mit n der Länge der Liste und k der Anzahl der kleinsten Elemente, die Sie extrahieren möchten. O (n), um die Liste zu häufen, und k-mal O (log (n)), um k Elemente zu platzieren. Dies ist besser als O (n * log (n)), insbesondere für kleine k.
Vortexfive
2
@Vortexfive und für Konstante k(wie hier mit 'zweitniedrigste') O(k*log(n)+n)vereinfacht sich zuO(n)
Duncan
5
Sie brauchen technisch gesehen kein Lambda für Einzeiler, aber es ist sicherlich bequemer. Hoffentlich wird später eine bessere Lambda-Unterstützung hinzugefügt.
James
1
@avpx: Es ist kein Heapsort. Wir pflegen nur einen 2-Element-Heap der 2 kleinsten angezeigten Elemente, anstatt die gesamte Liste zu häufen und alle Elemente wie bei einem Heapsort zu platzieren.
user2357112 unterstützt Monica
80

Das Einfügen der Anweisungen in eine Liste kann mehrere Anweisungen simulieren:

Z.B:

lambda x: [f1(x), f2(x), f3(x), x+1]
jmkg
quelle
9
Zur Verdeutlichung werden Funktionsaufrufe in Python nicht als Anweisungen betrachtet, sondern als Ausdrücke. Diese Liste ist also nur eine Liste von Ausdrücken, die alle sein können None.
Yawar
Ich denke, das funktioniert, weil der Interpreter bei der dynamischen Analyse der an das Lambda übergebenen Sammlung aufrufbare Signaturen erkennt und ausführt ... Sie können mit -> Lambda x testen: [print (x), print (x + 1), print (x) +2)]
ZEE
5
Noch besser: Lambda x: [f1 (x), f2 (x)] [- 1] gibt erwartungsgemäß das Berechnungsergebnis aus dem letzten Ausdruck zurück.
Anton Ovsyannikov
22

Zeitreisender hier. Wenn Sie im Allgemeinen mehrere Anweisungen in einem Lambda haben möchten, können Sie diesem Lambda andere Lambdas als Argumente übergeben.

(lambda x, f: list((y[1] for y in f(x))))(lst, lambda x: (sorted(y) for y in x))

Sie können nicht mehrere Anweisungen haben, aber Sie können dies simulieren, indem Sie Lambdas an Lambdas übergeben.

Bearbeiten: Der Zeitreisende kehrt zurück! Sie können auch das Verhalten von Booleschen Ausdrücken (unter Berücksichtigung von Kurzschlussregeln und Wahrhaftigkeit) für Kettenoperationen missbrauchen. Mit dem ternären Operator erhalten Sie noch mehr Leistung. Auch hier können Sie nicht mehrere Anweisungen haben , aber Sie können natürlich viele Funktionsaufrufe haben. In diesem Beispiel wird beliebiger Junk mit einer Reihe von Daten ausgeführt, aber es zeigt, dass Sie einige lustige Dinge tun können. Die print-Anweisungen sind Beispiele für Funktionen, die None(wie auch die .sort()Methode) zurückgegeben werden, aber sie helfen auch zu zeigen, was das lambdatut.

>>> (lambda x: print(x) or x+1)(10)
10
11
>>> f = (lambda x: x[::2] if print(x) or x.sort() else print(enumerate(x[::-1]) if print(x) else filter(lambda (i, y): print((i, y)) or (i % 3 and y % 2), enumerate(x[::-1]))))
>>> from random import shuffle
>>> l = list(range(100))
>>> shuffle(l)
>>> f(l)
[84, 58, 7, 99, 17, 14, 60, 35, 12, 56, 26, 48, 55, 40, 28, 52, 31, 39, 43, 96, 64, 63, 54, 37, 79, 25, 46, 72, 10, 59, 24, 68, 23, 13, 34, 41, 94, 29, 62, 2, 50, 32, 11, 97, 98, 3, 70, 93, 1, 36, 87, 47, 20, 73, 45, 0, 65, 57, 6, 76, 16, 85, 95, 61, 4, 77, 21, 81, 82, 30, 53, 51, 42, 67, 74, 8, 15, 83, 5, 9, 78, 66, 44, 27, 19, 91, 90, 18, 49, 86, 22, 75, 71, 88, 92, 33, 89, 69, 80, 38]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
(0, 99)
(1, 98)
(2, 97)
(3, 96)
(4, 95)
(5, 94)
(6, 93)
(7, 92)
(8, 91)
(9, 90)
(10, 89)
(11, 88)
(12, 87)
(13, 86)
(14, 85)
(15, 84)
(16, 83)
(17, 82)
(18, 81)
(19, 80)
(20, 79)
(21, 78)
(22, 77)
(23, 76)
(24, 75)
(25, 74)
(26, 73)
(27, 72)
(28, 71)
(29, 70)
(30, 69)
(31, 68)
(32, 67)
(33, 66)
(34, 65)
(35, 64)
(36, 63)
(37, 62)
(38, 61)
(39, 60)
(40, 59)
(41, 58)
(42, 57)
(43, 56)
(44, 55)
(45, 54)
(46, 53)
(47, 52)
(48, 51)
(49, 50)
(50, 49)
(51, 48)
(52, 47)
(53, 46)
(54, 45)
(55, 44)
(56, 43)
(57, 42)
(58, 41)
(59, 40)
(60, 39)
(61, 38)
(62, 37)
(63, 36)
(64, 35)
(65, 34)
(66, 33)
(67, 32)
(68, 31)
(69, 30)
(70, 29)
(71, 28)
(72, 27)
(73, 26)
(74, 25)
(75, 24)
(76, 23)
(77, 22)
(78, 21)
(79, 20)
(80, 19)
(81, 18)
(82, 17)
(83, 16)
(84, 15)
(85, 14)
(86, 13)
(87, 12)
(88, 11)
(89, 10)
(90, 9)
(91, 8)
(92, 7)
(93, 6)
(94, 5)
(95, 4)
(96, 3)
(97, 2)
(98, 1)
(99, 0)
[(2, 97), (4, 95), (8, 91), (10, 89), (14, 85), (16, 83), (20, 79), (22, 77), (26, 73), (28, 71), (32, 67), (34, 65), (38, 61), (40, 59), (44, 55), (46, 53), (50, 49), (52, 47), (56, 43), (58, 41), (62, 37), (64, 35), (68, 31), (70, 29), (74, 25), (76, 23), (80, 19), (82, 17), (86, 13), (88, 11), (92, 7), (94, 5), (98, 1)]
2rs2ts
quelle
7

Verwenden Sie die sortierte Funktion wie folgt:

map(lambda x: sorted(x)[1],lst)
alex vasi
quelle
ah.Thanks. (was für ein beschreibender Name für die Funktion!) Immer noch neugierig auf die mehrfache Aussage innerhalb des Lambda-Teils. Möglich?
Ottodidakt
1
Nein, "Funktionen, die mit Lambda-Formularen erstellt wurden, dürfen keine Anweisungen enthalten". docs.python.org/reference/expressions.html#lambda
alex vasi
1
-1: Lambda und Karte statt Listenverständnis? Nicht sehr pythonisch.
Nikow
@nikow [x [1] für x sortiert (Liste)] - pythonisch?
RaGa__M
@Explorer_N: Nicht die Liste muss sortiert werden, aber die Elemente der Liste (die Listen sind) müssen im vorliegenden Problem sortiert werden. Also eher : [sorted(x)[1] for x in list_of_lists].
Max
6

Sie können tatsächlich mehrere Anweisungen in einem Lambda-Ausdruck in Python haben. Es ist nicht ganz trivial, aber in Ihrem Beispiel funktioniert Folgendes:

map(lambda x: x.sort() or x[1],lst)

Sie müssen sicherstellen, dass jede Anweisung nichts zurückgibt oder wenn sie es einschließt (.. und False). Das Ergebnis ist das, was von der letzten Bewertung zurückgegeben wird.

Beispiel:

>>> f = (lambda : (print(1) and False) or (print(2) and False) or (print(3) and False))
>>> f()
1
2
3
Loreno Heer
quelle
5

Verwenden Sie begin () von hier aus: http://www.reddit.com/r/Python/comments/hms4z/ask_pyreddit_if_you_were_making_your_own/c1wycci

Python 3.2 (r32:88445, Mar 25 2011, 19:28:28) 
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> lst = [[567,345,234],[253,465,756, 2345],[333,777,111, 555]]
>>> begin = lambda *args: args[-1]
>>> list(map(lambda x: begin(x.sort(), x[1]), lst))
[345, 465, 333]
Janus Troelsen
quelle
4

Eine hackige Möglichkeit, mehrere Anweisungen in Python zu einer einzigen Anweisung zu kombinieren, besteht darin, das Schlüsselwort "und" als Kurzschlussoperator zu verwenden. Dann können Sie diese einzelne Anweisung direkt als Teil des Lambda-Ausdrucks verwenden.

Dies ähnelt der Verwendung von "&&" als Kurzschlussoperator in Shell-Sprachen wie bash.

Beachten Sie auch: Sie können jederzeit eine Funktionsanweisung korrigieren, um einen wahren Wert zurückzugeben, indem Sie die Funktion einschließen.

Beispiel:

def p2(*args):
    print(*args)
    return 1 # a true value

junky = lambda x, y: p2('hi') and p2('there') and p2(x) and p2(y)

junky("a", "b")

Beim zweiten Gedanken ist es wahrscheinlich besser, 'oder' anstelle von 'und' zu verwenden, da viele Funktionen bei Erfolg '0' oder Keine zurückgeben. Dann können Sie die Wrapper-Funktion im obigen Beispiel entfernen:

junky = lambda x, y: print('hi') or print('there') or print(x) or print(y)

junky("a", "b")

'und' operieren die Ausdrücke, bis der erste Null-Rückgabewert erreicht ist. Danach schließt es kurz. 1 und 1 und 0 und 1 ergibt: 1 und 1 und 0 und lässt 1 fallen

'oder' operiert die Ausdrücke, bis der erste Rückgabewert ungleich Null erreicht ist. Danach schließt es kurz.

0 oder 0 oder 1 oder 0 wertet 0 oder 0 oder 1 aus und lässt 0 fallen

Bill Moore
quelle
3

Oder wenn Sie Lambda vermeiden und einen Generator anstelle einer Liste haben möchten:

(sortiert (col) [1] für col in lst)

odwl
quelle
2

Lassen Sie mich Ihnen einen herrlichen, aber schrecklichen Hack präsentieren:

import types

def _obj():
  return lambda: None

def LET(bindings, body, env=None):
  '''Introduce local bindings.
  ex: LET(('a', 1,
           'b', 2),
          lambda o: [o.a, o.b])
  gives: [1, 2]

  Bindings down the chain can depend on
  the ones above them through a lambda.
  ex: LET(('a', 1,
           'b', lambda o: o.a + 1),
          lambda o: o.b)
  gives: 2
  '''
  if len(bindings) == 0:
    return body(env)

  env = env or _obj()
  k, v = bindings[:2]
  if isinstance(v, types.FunctionType):
    v = v(env)

  setattr(env, k, v)
  return LET(bindings[2:], body, env)

Sie können dieses LETFormular jetzt als solches verwenden:

map(lambda x: LET(('_', x.sort()),
                  lambda _: x[1]),
    lst)

was gibt: [345, 465, 333]

divs1210
quelle
Ursprünglich hier gepostet: gist.github.com/divs1210/d218d4b747b08751b2a232260321cdeb
divs1210
1

Sie können dies in O (n) -Zeit mit min und index anstelle von sort oder heapq tun.

Erstellen Sie zuerst eine neue Liste von allem außer dem Mindestwert der ursprünglichen Liste:

new_list = lst[:lst.index(min(lst))] + lst[lst.index(min(lst))+1:]

Nehmen Sie dann den Mindestwert der neuen Liste:

second_smallest = min(new_list)

Jetzt alles zusammen in einem einzigen Lambda:

map(lambda x: min(x[:x.index(min(x))] + x[x.index(min(x))+1:]), lst)

Ja, es ist wirklich hässlich, aber es sollte algorithmisch billig sein. Auch da einige Leute in diesem Thread Listenverständnisse sehen wollen:

[min(x[:x.index(min(x))] + x[x.index(min(x))+1:]) for x in lst]
nacktfanatisch
quelle
1

Genau dafür wird die bindFunktion in einer Monade verwendet.

Mit der bindFunktion können Sie mehrere Lambdas zu einem Lambda kombinieren, wobei jedes Lambda eine Aussage darstellt.

jhegedus
quelle
1

Es gibt tatsächlich eine Möglichkeit, mehrere Anweisungen in Lambda zu verwenden. Hier ist meine Lösung:

lst = [[567,345,234],[253,465,756, 2345],[333,777,111, 555]]

x = lambda l: exec("l.sort(); return l[1]")

map(x, lst)
EternalCrafter_606
quelle
Der Code im Inneren execkann nicht von einer intelligenten IDE
überprüft werden
0

Ja. Sie können es auf diese Weise definieren und dann Ihre mehreren Ausdrücke wie folgt umschließen:

Schema beginnen:

begin = Lambda * x: x [-1]

Gemeinsame Lisp-Prognose:

Progn = Lambda * x: x [-1]

Anastasios
quelle
0

Es gibt bessere Lösungen ohne Lambda-Funktion. Wenn wir jedoch wirklich die Lambda-Funktion verwenden möchten, finden Sie hier eine generische Lösung für mehrere Anweisungen: map (Lambda x: x [1] if (x.sort ()) else x [1], lst)

Es ist dir egal, was die Anweisung zurückgibt.

user11166890
quelle
0

Ja, es ist möglich. Versuchen Sie es mit dem folgenden Code-Snippet.

x = [('human', 1), ('i', 2), ('am', 1), ('.', 1), ('love', 1), ('python', 3), ('', 1),
  ('run', 1), ('is', 2), ('robust', 1), ('hello', 1), ('spark', 2), ('to', 1), ('analysis', 2), ('on', 1), ('big', 1), ('data', 1), ('with', 1), ('analysis', 1), ('great', 1)
]

rdd_filter = rdd1_word_cnt_sum.filter(lambda x: 'python' in x or 'human' in x or 'big' in x)
rdd_filter.collect()
Ravi
quelle
-1

Ich gebe Ihnen eine andere Lösung: Lassen Sie Ihr Lambda eine Funktion aufrufen.

def multiple_statements(x, y):
    print('hi')
    print('there')
    print(x)
    print(y)
    return 1

junky = lambda x, y: multiple_statements(x, y)

junky('a', 'b');
Bill Moore
quelle
7
Noch besser : junky = multiple_statements.
Solomon Ucko