2010-12-06 3 views
8

Sehr selten werde ich auf Code in Python stoßen, der eine anonyme Funktion verwendet, die eine anonyme Funktion zurückgibt ...?Lambda gibt Lambda in Python zurück

Leider kann ich kein Beispiel auf der Hand finden, aber es dauert in der Regel die Form wie folgt aus:

g = lambda x,c: x**c lambda c: c+1 

Warum sollte jemand das tun? Vielleicht kannst du ein Beispiel geben, das Sinn macht (ich bin mir nicht sicher, ob das, was ich gemacht habe, einen Sinn ergibt).

Edit: Hier ist ein Beispiel:

swap = lambda a,x,y:(lambda f=a.__setitem__:(f(x,(a[x],a[y])), 
     f(y,a[x][0]),f(x,a[x][1])))() 
+3

finden und zeigen echtes Beispiel; das ist nicht syntaktisch korrekt. –

+0

erzeugt SyntaxError: ungültige Syntax. Kein Sinn zweites Lambda – joaquin

+0

Warum sollte jemand das tun? Antwort: Andere irritieren? – joaquin

Antwort

13

Sie ein solches Konstrukt verwenden könnte currying zu tun:

curry = lambda f, a: lambda x: f(a, x) 

Vielleicht haben Sie es verwenden möchten:

>>> add = lambda x, y: x + y 
>>> add5 = curry(add, 5) 
>>> add5(3) 
8 
+0

Dies ist im Allgemeinen nicht besser mit functools.partial getan? – joaquin

+0

@joaquin: Ja. Ich präsentiere nur ein Beispiel. –

1

Es kann für temporäre Platzhalter nützlich sein. Angenommen, Sie ein Dekorateur Fabrik:

@call_logger(log_arguments=True, log_return=False) 
def f(a, b): 
    pass 

Sie können es vorübergehend ersetzen mit

call_logger = lambda *a, **kw: lambda f: f 

Es kann auch nützlich sein, wenn es indirekt eine Lambda zurück:

import collections 
collections.defaultdict(lambda: collections.defaultdict(lambda: collections.defaultdict(int))) 

Es ist auch nützlich für Erstellen von aufrufbaren Fabriken in der Python-Konsole.

Und nur weil etwas möglich ist, bedeutet das nicht, dass Sie es verwenden müssen.

1

Dies kann verwendet werden, um einige häufige repetitive Code zu ziehen (es gibt natürlich andere Möglichkeiten, dies in Python zu erreichen).

Vielleicht schreiben Sie einen Logger, und Sie müssen die Ebene der Protokollzeichenfolge voranstellen.

import sys 
prefixer = lambda prefix: lambda message: sys.stderr.write(prefix + ":" + message + "\n") 
log_error = prefixer("ERROR") 
log_warning = prefixer("WARNING") 
log_info = prefixer("INFO") 
log_debug = prefixer("DEBUG") 

log_info("An informative message") 
log_error("Oh no, a fatal problem") 

Dieses Programm druckt

INFO:An informative message 
    ERROR:Oh no, a fatal problem 
1

Ich habe so etwas wie dies nur den anderen Tag ein Testverfahren in einer Unittest-Suite zu deaktivieren: Sie könnte so etwas wie schreiben.

disable = lambda fn : lambda *args, **kwargs: None 

@disable 
test_method(self): 
    ... test code that I wanted to disable ... 

Einfach, es später wieder zu aktivieren.

2
swap = lambda a,x,y:(lambda f=a.__setitem__:(f(x,(a[x],a[y])), 
     f(y,a[x][0]),f(x,a[x][1])))() 

Siehe die() am Ende? Das innere Lambda wird nicht zurückgegeben, es wird aufgerufen.

Die Funktion tut das Äquivalent von

def swap(a, x, y): 
    a[x] = (a[x], a[y]) 
    a[y] = a[x][0] 
    a[x] = a[x][1] 

Aber nehmen wir an, dass wir dies in einem Lambda tun wollen. Wir können keine Zuweisungen in einem Lambda verwenden. Wir können jedoch __setitem__ für den gleichen Effekt aufrufen.

def swap(a, x, y): 
    a.__setitem__(x, (a[x], a[y])) 
    a.__setitem__(y, a[x][0]) 
    a.__setitem__(x, a[x][1]) 

Aber für ein Lambda können wir nur einen Ausdruck haben. Aber da diese Funktionsaufrufe sind, können wir sie in einem Tupel einpacken

def swap(a, x, y): 
    (a.__setitem__(x, (a[x], a[y])), 
    a.__setitem__(y, a[x][0]), 
    a.__setitem__(x, a[x][1])) 

jedoch all diese __setitem__ ‚s mir sind immer nach unten, so lasst sie ausklammern:

def swap(a, x, y): 
    f = a.__setitem__ 
    (f(x, (a[x], a[y])), 
    f(y, a[x][0]), 
    f(x, a[x][1])) 

Dagnamit, kann ich Geh nicht mit dem Hinzufügen einer anderen Aufgabe! Ich weiß, lass uns Standardparameter missbrauchen.

def swap(a, x, y): 
    def inner(f = a.__setitem__): 
     (f(x, (a[x], a[y])), 
     f(y, a[x][0]), 
     f(x, a[x][1])) 
    inner() 

Ok wir über lambdas wechseln:

swap = lambda a, x, y: lambda f = a.__setitem__: (f(x, (a[x], a[y])), f(y, a[x][0]), f(x, a[x][1]))() 

Was uns wieder auf den ursprünglichen Ausdruck bringt (plus/minus Tippfehler)

All dies führt zu der Frage zurück: Warum?

Die Funktion sollte als

def swap(a, x, y): 
    a[x],a[y] = a[y],a[x] 

Der ursprüngliche Autor ging Art und Weise aus dem Weg umgesetzt wurden eher eine Lambda zu verwenden, um eine Funktion. Es könnte sein, dass er aus irgendeinem Grund die verschachtelte Funktion nicht mag. Ich weiß es nicht. Alles, was ich sagen werde, ist sein schlechter Code. (es sei denn, es gibt eine mysteriöse Begründung dafür.)

0

Es ist meistens - zumindest in Code ich komme und dass ich selbst schreibe - verwendet, um eine Variable mit dem Wert "einzufrieren", den es an dem Punkt hat das Lambda Funktion wird erstellt. Andernfalls verweist die Variable nonlocals auf eine Variable in dem Bereich, in dem sie existieren, was manchmal zu nicht gewünschten Ergebnissen führen kann.

Zum Beispiel, wenn ich eine Liste von zehn Funktionen erstellen möchten, die jeweils ein Multiplikator für einen skalaren von 0 bis 9. Ein Wesen könnte versucht sein, es so zu schreiben:

>>> a = [(lambda j: i * j) for i in range(10)] 
>>> a[9](10) 
90 

Wer, wenn Sie eine der anderen factoried Funktionen verwenden möchten, erhalten Sie das gleiche Ergebnis:

>>> a[1](10) 
90 

das liegt daran, dass die „i“ Variable innerhalb der Lambda nicht aufgelöst wird, wenn die Lambda erzeugt wird. Stattdessen behält Python einen Verweis auf das "i" in der "for" -Anweisung - auf dem Bereich, in dem es erstellt wurde (diese Referenz wird in der lambda-Funktion closure beibehalten). Wenn das Lambda ausgeführt wird, wird die Variable ausgewertet und ihr Wert ist der letzte in diesem Bereich.

Wenn man zwei ineinander geschachtelten Lambdas wie folgt verwendet:

>>> a = [(lambda k: (lambda j: k * j))(i) for i in range(10)] 

Der "i" variable durint der Ausführung des "for" -Schleife ausgewertet wird. Der It¶-Wert wird an "k" übergeben - und "k" wird als nicht lokale Variable in der Multiplikatorfunktion verwendet, die wir ausschließen. Für jeden Wert von i gibt es eine andere Instanz der umschließenden Lambda-Funktion und einen anderen Wert für die Variable "k".

So ist es möglich, die ursprüngliche Absicht zu erreichen:

>>> a = [(lambda k: (lambda j: k * j))(i) for i in range(10)] 
>>> a[1](10) 
10 
>>> a[9](10) 
90 
>>> 
+0

Warum nicht verwenden: a = [(lambda k, i = i: i * j) für j im Bereich (10)] stattdessen? –