it-swarm.com.de

Was ist für die Verwendung in Python vorzuziehen: Lambda-Funktionen oder verschachtelte Funktionen ('def')?

Ich verwende meistens Lambda-Funktionen, aber manchmal verschachtelte Funktionen, die das gleiche Verhalten zu haben scheinen. 

Hier einige triviale Beispiele, bei denen sie funktionell dasselbe tun, wenn beide in einer anderen Funktion gefunden wurden:

Lambda-Funktion

>>> a = lambda x : 1 + x
>>> a(5)
6

Verschachtelte Funktion

>>> def b(x): return 1 + x

>>> b(5)
6

Gibt es Vorteile bei der Verwendung von einem über dem anderen? (Leistung? Lesbarkeit? Einschränkungen? Konsistenz? Usw.) 

Ist es überhaupt wichtig? Wenn dies nicht der Fall ist, verstößt dies gegen das Pythonic-Prinzip: 

"Es sollte einen geben - und vorzugsweise nur einen - offensichtlichen Weg, dies zu tun" .

83
Ray Vega

Wenn Sie die lambda einem Namen zuweisen müssen, verwenden Sie stattdessen eine def. defs sind nur syntaktischer Zucker für eine Zuweisung, das Ergebnis ist also das gleiche und sie sind viel flexibler und lesbarer.

lambdas kann einmal für use verwendet werden, -Funktionen wegwerfen, die keinen Namen haben.

Dieser Anwendungsfall ist jedoch sehr selten. Sie müssen selten unbenannte Funktionsobjekte umleiten.

Die eingebauten Funktionen map() und filter() benötigen Funktionsobjekte, aber Listenverständnisund Generatorausdrücke sind im Allgemeinen besser lesbar als diese Funktionen und können alle Anwendungsfälle abdecken, ohne dass Lambdas benötigt werden. 

Für den Fall, dass Sie wirklich ein kleines Funktionsobjekt benötigen, sollten Sie die operator-Modulfunktionen wie operator.add anstelle von lambda x, y: x + y verwenden.

Wenn Sie dennoch eine lambda benötigen, die nicht abgedeckt ist, können Sie eine def schreiben, um nur lesbarer zu sein. Ist die Funktion komplexer als bei operator, ist eine def wahrscheinlich besser. 

Daher sind echte Anwendungsfälle mit lambda sehr selten.

87
nosklo

Für mich gibt es praktisch zwei Unterschiede:

Beim ersten geht es darum, was sie tun und was sie zurückgeben:

  • def ist ein Schlüsselwort, das nichts zurückgibt und einen Namen im lokalen Namespace erstellt.

  • lambda ist ein Schlüsselwort, das ein Funktionsobjekt zurückgibt und im lokalen Namespace keinen Namen erstellt.

Wenn Sie also eine Funktion aufrufen müssen, die ein Funktionsobjekt verwendet, können Sie dies in einer Zeile mit Python-Code nur mit einem Lambda tun. Es gibt kein Äquivalent mit def.

In einigen Rahmen ist dies tatsächlich üblich; Zum Beispiel benutze ich Twisted viel und mache so etwas

d.addCallback(lambda result: setattr(self, _someVariable, result))

ist ziemlich üblich und mit Lambdas prägnanter.

Der zweite Unterschied betrifft das, was die eigentliche Funktion tun darf.

  • Eine mit 'def' definierte Funktion kann beliebigen Python-Code enthalten
  • Eine mit 'Lambda' definierte Funktion muss zu einem Ausdruck ausgewertet werden und darf daher keine Anweisungen wie print, import, raise, ... enthalten. 

Zum Beispiel,

def p(x): print x

funktioniert wie erwartet, während

lambda x: print x

ist ein SyntaxError.

Natürlich gibt es Problemumgehungen - ersetzen Sie print durch sys.stdout.write oder import durch __import__. In der Regel ist es in diesem Fall jedoch besser, mit einer Funktion zu gehen.

In diesem Interview, Guido van Rossum sagt, er wünscht, er hätte "Lambda" nicht in Python gelassen:

"Q. Mit welcher Funktion von Python sind Sie am wenigsten zufrieden?

Manchmal war ich zu schnell bei der Annahme von Beiträgen, und später wurde mir klar, dass dies ein Fehler war. Ein Beispiel wären einige der funktionalen Programmierungsfunktionen, wie z. B. Lambda-Funktionen. Lambda ist ein Schlüsselwort, mit dem Sie eine kleine anonyme Funktion erstellen können. Integrierte Funktionen, wie z. B. Zuordnen, Filtern und Verkleinern, führen eine Funktion über einen Sequenztyp aus, z. B. eine Liste.

In der Praxis war es nicht so gut. Python hat nur zwei Bereiche: lokal und global. Dies macht das Schreiben von Lambda-Funktionen mühsam, da Sie häufig auf Variablen in dem Bereich zugreifen möchten, in dem das Lambda definiert wurde. Dies ist jedoch aufgrund der beiden Bereiche nicht möglich. Es gibt einen Weg, um dies zu umgehen, aber es ist ein bisschen kludge. In Python scheint es oft viel einfacher zu sein, nur eine for-Schleife zu verwenden, anstatt mit Lambda-Funktionen herumzuspielen. Karte und Freunde funktionieren nur dann gut, wenn bereits eine integrierte Funktion vorhanden ist, die das tut, was Sie möchten.

IMHO kann Iambdas manchmal bequem sein, ist aber normalerweise auf Kosten der Lesbarkeit bequem. Kannst du mir sagen, was das bedeutet?

str(reduce(lambda x,y:x+y,map(lambda x:x**x,range(1,1001))))[-10:]

Ich habe es geschrieben, und ich habe eine Minute gebraucht, um es herauszufinden. Dies ist von Project Euler - ich werde nicht sagen, welches Problem ich habe, weil ich Spoiler hasse, aber es läuft in 0,124 Sekunden :)

19
Chris Lawlor

Für n = 1000 gibt es einige Möglichkeiten, eine Funktion im Vergleich zu einem Lambda aufzurufen:

In [11]: def f(a, b):
             return a * b

In [12]: g = lambda x, y: x * y

In [13]: %%timeit -n 100
for a in xrange(n):
  for b in xrange(n):
    f(a, b)
   ....:
100 loops, best of 3: 285 ms per loop

In [14]: %%timeit -n 100
for a in xrange(n):
  for b in xrange(n):
    g(a, b)
   ....:
100 loops, best of 3: 298 ms per loop

In [15]: %%timeit -n 100
for a in xrange(n):
  for b in xrange(n):
    (lambda x, y: x * y)(a, b)
   ....:
100 loops, best of 3: 462 ms per loop
9
Andy Hayden

Ich stimme der Empfehlung von nosklo zu: Wenn Sie der Funktion einen Namen geben müssen, verwenden Sie def. Ich reserviere lambda-Funktionen für Fälle, in denen ich nur einen kurzen Code-Code an eine andere Funktion weitergebe, z.

a = [ (1,2), (3,4), (5,6) ]
b = map( lambda x: x[0]+x[1], a )
6
Dan Lenski

Leistung:

Das Erstellen einer Funktion mit lambda ist etwas schneller als das Erstellen mit def. Der Unterschied besteht darin, dass def einen Namenseintrag in der lokalen Tabelle erstellt. Die resultierende Funktion hat die gleiche Ausführungsgeschwindigkeit.


Lesbarkeit:

Lambda-Funktionen sind für die meisten Python-Benutzer etwas weniger lesbar, in manchen Fällen aber auch viel prägnanter. Erwägen Sie die Umstellung von nicht funktionaler auf funktionale Routine:

# Using non-functional version.

heading(math.sqrt(v.x * v.x + v.y * v.y), math.atan(v.y / v.x))

# Using lambda with functional version.

fheading(v, lambda v: math.sqrt(v.x * v.x + v.y * v.y), lambda v: math.atan(v.y / v.x))

# Using def with functional version.

def size(v):
    return math.sqrt(v.x * v.x + v.y * v.y)

def direction(v):
    return math.atan(v.y / v.x)

deal_with_headings(v, size, direction)

Wie Sie sehen, ist die lambda-Version kürzer und "einfacher" in dem Sinne, dass Sie nur lambda v: zur ursprünglichen nicht-funktionalen Version hinzufügen müssen, um in die funktionale Version zu konvertieren. Es ist auch viel prägnanter. Denken Sie jedoch daran, dass viele Python-Benutzer durch die Lambda-Syntax verwirrt werden. Das, was Sie an Länge und realer Komplexität verlieren, könnte von anderen Programmierern verwirrt werden.


Einschränkungen:

  • lambda-Funktionen können nur einmal verwendet werden, sofern sie nicht einem Variablennamen zugewiesen sind.
  • lambda-Funktionen, die Variablennamen zugewiesen sind, haben keinen Vorteil gegenüber def-Funktionen.
  • lambda-Funktionen können schwierig oder unmöglich zu beizen sein.
  • Die Namen der def-Funktionen müssen sorgfältig ausgewählt werden, um angemessen beschreibend und eindeutig zu sein oder zumindest im Umfang nicht verwendet zu werden.

Konsistenz:

Python vermeidet meistens funktionale Programmierkonventionen zugunsten der prozeduralen und einfacheren objektiven Semantik. Der Operator lambda steht in direktem Gegensatz zu diesem Vorurteil. Als Alternative zu der bereits vorherrschenden def fügt die lambda-Funktion Ihrer Syntax Diversität hinzu. Einige würden das weniger konsistent betrachten.


Bereits vorhandene Funktionen:

Wie von anderen angemerkt, können viele Verwendungen von lambda im Feld durch Mitglieder der operator oder anderer Module ersetzt werden. Zum Beispiel:

do_something(x, y, lambda x, y: x + y)
do_something(x, y, operator.add)

Durch die Verwendung der bereits vorhandenen Funktion kann Code in vielen Fällen lesbarer werden.


Das pythonische Prinzip: "Es sollte einen - und vorzugsweise nur einen - naheliegenden Weg geben"

Das ist der einzigen Quelle der Wahrheit Doktrin ähnlich. Unglücklicherweise war das Prinzip des Single-offensichtlichen Weg-zu-Es-Tuns immer ein wehmütiges Streben für Python und nicht ein wahrer Leitsatz. Betrachten Sie die sehr leistungsfähigen Array-Verstehen in Python. Sie sind funktional äquivalent zu den Funktionen map und filter:

[e for e in some_array if some_condition(e)]
filter(some_array, some_condition)

lambda und def sind gleich.

Es ist Ansichtssache, aber ich würde sagen, dass alles in der Python-Sprache, die für den allgemeinen Gebrauch bestimmt ist und offensichtlich nichts kaputt macht, "Pythonic" genug ist.

5
Pi Marillion

Obwohl es mit den anderen Antworten übereinstimmt, ist es manchmal lesbarer. Hier ist ein Beispiel, wo lambda nützlich ist. In einem Anwendungsfall stelle ich immer wieder eine N-dimensionale defaultdict vor.
Hier ist ein Beispiel:

from collections import defaultdict
d = defaultdict(lambda: defaultdict(list))
d['Foo']['Bar'].append(something)

Ich finde es lesbarer als das Erstellen einer def für die zweite Dimension. Dies gilt umso mehr für höhere Dimensionen.

3
Jonathan

Lambda wurde vor allem für einfache Callback-Funktionen verwendet und für das Zuordnen, Reduzieren und Filtern, die eine Funktion als Argument benötigen. Mit Listenverständnissen wird die Norm, und das Hinzufügen ist zulässig, wenn wie in:

x = [f for f in range(1, 40) if f % 2]

es ist schwer vorstellbar, was für den täglichen Gebrauch von Lambda der Fall ist. Daher würde ich sagen, vermeiden Sie Lambda und erstellen Sie verschachtelte Funktionen.

3
apg

Eine wichtige Einschränkung von Lambdas ist, dass sie außer einem Ausdruck nichts enthalten dürfen. Es ist fast unmöglich, dass ein Lambda-Ausdruck neben unbedeutenden Nebenwirkungen irgendetwas produziert, da er nicht annähernd so reichhaltig sein kann wie eine def-Funktion.

Davon abgesehen, beeinflusste Lua meinen Programmierstil in Bezug auf die umfassende Verwendung anonymer Funktionen, und ich schmiere meinen Code damit. Darüber hinaus neige ich dazu, über Map/Reduction als abstrakte Operatoren in einer Weise nachzudenken, die ich nicht für Listenverständnisse oder Generatoren halte, fast so, als würde ich eine Implementierungsentscheidung explizit mit diesen Operatoren verschieben. 

Edit: Dies ist eine ziemlich alte Frage, und meine Meinungen dazu haben sich etwas geändert.

Zuallererst bin ich stark geneigt, einer Variablen einen lambda-Ausdruck zuzuweisen; as python hat dafür eine spezielle Syntax (Hinweis, def). Darüber hinaus haben viele Anwendungen für Lambda vordefinierte (und effizientere) Implementierungen, auch wenn sie keinen Namen erhalten. Das fragliche Beispiel kann beispielsweise als (1).__add__ abgekürzt werden, ohne dass es in lambda oder def eingepackt werden muss. Viele andere gebräuchliche Anwendungen können mit einer Kombination der Module operator, itertools und functools erfüllt werden.

Eine Anwendung für Lambdas, die ich gefunden habe, ist in Debugmeldungen.

Da Lambdas träge ausgewertet werden können, können Sie Code wie folgt verwenden:

log.debug(lambda: "this is my message: %r" % (some_data,))

statt möglicherweise teuer:

log.debug("this is my message: %r" % (some_data,))

die den Formatstring verarbeitet, auch wenn der Debug-Aufruf aufgrund der aktuellen Protokollierungsstufe keine Ausgabe erzeugt.

Damit es wie beschrieben funktioniert, muss das Protokollierungsmodul Lambdas natürlich als "faule Parameter" unterstützen (wie mein Protokollierungsmodul).

Die gleiche Idee kann auf jeden anderen Fall einer verzögerten Bewertung für die Erstellung von Inhaltswerten auf Abruf angewendet werden.

Zum Beispiel dieser benutzerdefinierte ternäre Operator:

def mif(condition, when_true, when_false):
    if condition:
         return when_true()
    else:
         return when_false()

mif(a < b, lambda: a + a, lambda: b + b)

anstatt:

def mif(condition, when_true, when_false):
    if condition:
         return when_true
    else:
         return when_false

mif(a < b, a + a, b + b)

bei Lambdas wird nur der von der Bedingung ausgewählte Ausdruck ausgewertet, ohne Lambdas werden beide ausgewertet.

Natürlich können Sie einfach Funktionen anstelle von Lambdas verwenden, aber für kurze Ausdrücke sind Lambdas (c) magerer.

2
Glushiator

Bevorzugter: Lambda-Funktionen oder verschachtelte Funktionen (def)?

Die Verwendung eines Lambda gegenüber einer regulären Funktion (sie werden in einem Ausdruck erstellt) bietet einen Vorteil und hat mehrere Nachteile. Aus diesem Grund ziehe ich es vor, Funktionen mit dem Schlüsselwort def anstatt mit Lambdas zu erstellen.

Erster Punkt - es handelt sich um denselben Objekttyp

Ein Lambda führt zu demselben Objekttyp wie eine reguläre Funktion

>>> l = lambda: 0
>>> type(l)
<class 'function'>
>>> def foo(): return 0
... 
>>> type(foo)
<class 'function'>
>>> type(foo) is type(l)
True

Da Lambdas Funktionen sind, sind sie erstklassige Objekte.

Sowohl Lambdas als auch Funktionen:

  • kann als Argument übergeben werden (wie eine reguläre Funktion)
  • wenn sie innerhalb einer äußeren Funktion erstellt werden, werden sie zu den lokalen Funktionen der äußeren Funktionen

Bei Lambdas fehlen jedoch standardmäßig einige Dinge, die Funktionen über die vollständige Funktionsdefinitionssyntax erhalten.

__name__ eines Lambas ist '<lambda>'

Lambdas sind schließlich anonyme Funktionen, so dass sie ihren eigenen Namen nicht kennen.

>>> l.__name__
'<lambda>'
>>> foo.__name__
'foo'

Daher können Lambdas in ihrem Namensraum nicht programmatisch nachgeschlagen werden.

Dies begrenzt bestimmte Dinge. Zum Beispiel kann foo mit serialisiertem Code nachgeschlagen werden, während l nicht:

>>> import pickle
>>> pickle.loads(pickle.dumps(l))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
_pickle.PicklingError: Can't pickle <function <lambda> at 0x7fbbc0464e18>: 
attribute lookup <lambda> on __main__ failed

Wir können foo gut nachschlagen - weil es seinen eigenen Namen kennt:

>>> pickle.loads(pickle.dumps(foo))
<function foo at 0x7fbbbee79268>

Lambdas haben keine Anmerkungen und keine Docstring

Im Prinzip sind Lambdas nicht dokumentiert. Lassen Sie uns foo neu schreiben, um besser dokumentiert zu werden:

def foo() -> int:
    """a nullary function, returns 0 every time"""
    return 0

Jetzt hat foo eine Dokumentation:

>>> foo.__annotations__
{'return': <class 'int'>}
>>> help(foo)
Help on function foo in module __main__:

foo() -> int
    a nullary function, returns 0 every time

Wir haben jedoch nicht den gleichen Mechanismus, um Lambdas die gleichen Informationen zu geben:

>>> help(l)
Help on function <lambda> in module __main__:

<lambda> lambda (...)

Aber wir können sie hacken:

>>> l.__doc__ = 'nullary -> 0'
>>> l.__annotations__ = {'return': int}
>>> help(l)
Help on function <lambda> in module __main__:

<lambda> lambda ) -> in
    nullary -> 0

Es gibt jedoch wahrscheinlich einen Fehler, der die Ausgabe der Hilfe durcheinander bringt.

Lambdas können nur einen Ausdruck zurückgeben

Lambdas können keine komplexen Anweisungen zurückgeben, nur Ausdrücke.

>>> lambda: if True: 0
  File "<stdin>", line 1
    lambda: if True: 0
             ^
SyntaxError: invalid syntax

Ausdrücke können zwar ziemlich komplex sein, und wenn Sie very hard versuchen, können Sie wahrscheinlich dasselbe mit einem Lambda erreichen, aber die hinzugefügte Komplexität wirkt sich eher nachteilig auf das Schreiben von klarem Code aus. 

Wir verwenden Python für Klarheit und Wartungsfreundlichkeit. Eine Überbeanspruchung von Lambdas kann dagegen wirken.

Das only -Doppelseite für Lambdas: kann in einem einzigen Ausdruck erstellt werden

Dies ist das einzig mögliche Aufwärtspotenzial. Da Sie ein Lambda mit einem Ausdruck erstellen können, können Sie es innerhalb eines Funktionsaufrufs erstellen. 

Durch das Erstellen einer Funktion innerhalb eines Funktionsaufrufs wird die (kostengünstige) Namenssuche im Vergleich zu einer an anderer Stelle erstellten Suche vermieden. 

Da Python jedoch streng ausgewertet wird, gibt es keinen weiteren Leistungsgewinn, außer dass die Namenssuche vermieden wird.

Für einen sehr einfachen Ausdruck wähle ich vielleicht ein Lambda.

Ich neige auch dazu, beim interaktiven Python Lambdas zu verwenden, um mehrere Zeilen zu vermeiden, wenn dies der Fall ist. Ich verwende die folgende Art von Code-Format, wenn ich beim Aufruf von timeit.repeat ein Argument an einen Konstruktor übergeben möchte:

import timeit

def return_nullary_lambda(return_value=0):
    return lambda: return_value

def return_nullary_function(return_value=0):
    def nullary_fn():
        return return_value
    return nullary_fn

Und nun:

>>> min(timeit.repeat(lambda: return_nullary_lambda(1)))
0.24312214995734394
>>> min(timeit.repeat(lambda: return_nullary_function(1)))
0.24894469301216304

Ich glaube, dass der leichte Zeitunterschied oben auf die Namenssuche in return_nullary_function zurückzuführen ist - beachten Sie, dass es very vernachlässigbar ist.

Fazit

Lambdas eignen sich gut für informelle Situationen, in denen Sie die Codezeilen zugunsten einer eindeutigen Aussage minimieren möchten.

Lambdas sind schlecht für formalere Situationen, in denen Sie Klarheit für Code-Editoren benötigen, die später kommen werden, insbesondere wenn sie nicht trivial sind.

Wir wissen, dass wir unseren Objekten gute Namen geben sollen. Wie können wir das tun, wenn das Objekt no name hat?

Aus all diesen Gründen möchte ich lieber Funktionen mit def anstatt mit lambda erstellen.

2
Aaron Hall

Wenn Sie das Lambda nur einer Variablen im lokalen Gültigkeitsbereich zuweisen, können Sie auch def verwenden, da es lesbarer ist und in der Zukunft einfacher erweitert werden kann:

fun = lambda a, b: a ** b # a pointless use of lambda
map(fun, someList)

oder

def fun(a, b): return a ** b # more readable
map(fun, someList)
1
too much php
  • Rechenzeit.
  • Funktion ohne Namen.
  • Um eine Funktion zu erreichen, verwenden viele Funktionen.

Betrachten wir ein einfaches Beispiel

# CREATE ONE FUNCTION AND USE IT TO PERFORM MANY OPERATIONS ON SAME TYPE OF DATA STRUCTURE.
def variousUse(a,b=lambda x:x[0]):
    return [b(i) for i in a]

dummyList = [(0,1,2,3),(4,5,6,7),(78,45,23,43)]
variousUse(dummyList)                           # extract first element
variousUse(dummyList,lambda x:[x[0],x[2],x[3]]) # extract specific indexed element
variousUse(dummyList,lambda x:x[0]+x[2])        # add specific elements
variousUse(dummyList,lambda x:x[0]*x[2])        # multiply specific elements
0
bhargav patel

Ich stimme mit Nosklo überein. Übrigens, selbst mit einer einmal verwenden, wegwerfen Funktion, möchten Sie meistens nur etwas vom Bedienmodul verwenden.

Z.B : 

Sie haben eine Funktion mit dieser Signatur: myFunction (Daten, Callback-Funktion).

Sie möchten eine Funktion übergeben, die 2 Elemente hinzufügt.

Mit Lambda:

myFunction(data, (lambda x, y : x + y))

Der pythonische Weg:

import operator
myFunction(data, operator.add)

Natürlich ist dies ein einfaches Beispiel, aber es gibt eine Menge Dinge, die das Operator-Modul bereitstellt, einschließlich der Elemente, die für Liste und Dikt gesetzt werden. Wirklich cool.

0
e-satis