Welches ist mehr bevorzugt, in Python zu verwenden: Lambda-Funktionen oder verschachtelte Funktionen ( ‚def‘)?

StackOverflow https://stackoverflow.com/questions/134626

Frage

ich meist Lambda-Funktionen verwenden, aber manchmal verschachtelte Funktionen verwenden, die das gleiche Verhalten zu bieten scheinen.

Hier sind einige triviale Beispiele, in denen sie funktionell das gleiche tun, wenn entweder innerhalb einer anderen Funktion gefunden:

Lambda-Funktion

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

Verschachtelte Funktion

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

>>> b(5)
6

Gibt es Vorteile auf einen über den anderen verwenden? (Performance? Ablesbarkeit? Einschränkungen? Consistency? Etc.)

Spielt es eine Rolle, selbst? Wenn es dann nicht funktioniert hat, die verletzen das Prinzip Pythonic:

  

„sollte es sein, ein und vorzugsweise nur eine offensichtliche Art und Weise, es zu tun“ .

War es hilfreich?

Lösung

Wenn Sie die lambda auf einen Namen zuweisen müssen, eine def stattdessen verwenden. defs sind nur syntaktischer Zucker für eine Zuordnung, so dass das Ergebnis ist das gleiche, und sie sind viel flexibler und lesbar.

lambdas kann verwendet werden für einmal verwenden, wegzuwerfen Funktionen, die einen Namen nicht haben wird.

Dies ist jedoch Anwendungsfall ist sehr selten. Sie müssen nur selten um unbenannte Funktion Objekte zu übergeben.

Die builtins map() und filter() Notwendigkeit Funktionsobjekte, aber Listenkomprehensionen und Generator Ausdrücke sind in der Regel besser lesbar als die Funktionen und kann alle Anwendungsfälle abdecken, ohne die Notwendigkeit von Lambda-Ausdrücke.

Für die Fälle, die Sie wirklich brauchen ein kleines Funktionsobjekt, sollten Sie die operator Modulfunktionen nutzen, wie operator.add statt lambda x, y: x + y

Wenn Sie noch etwas lambda nicht abgedeckt benötigen, könnten Sie ein def schreiben, nur besser lesbar sein. Wenn die Funktion komplexer als die, die bei operator Modul ist, ist ein def wahrscheinlich besser.

So, reale Welt gut lambda Anwendungsfälle sind sehr selten.

Andere Tipps

Praktisch gesprochen, mir gibt es zwei Unterschiede:

Die erste ist, was sie tun und was sie zurückgeben:

  • def ist ein Schlüsselwort, das nichts zurückliefert und erzeugt einen 'Namen' in der lokalen Namensraum.

  • Lambda ist ein Schlüsselwort, das eine Funktion Objekt zurückgibt und einen ‚Namen‘ in dem lokalen Namensraum nicht erstellen.

Wenn Sie also eine Funktion aufrufen müssen, die eine Funktion Aufgabe übernimmt, die einzige Möglichkeit, mit einem Lambda in einer Zeile von Python-Code zu tun ist. Es gibt kein Äquivalent mit def.

In einigen Frameworks diese eigentlich recht häufig sind; zum Beispiel verwende ich Verdrehte viel , und so etwas wie

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

ist durchaus üblich, und prägnanter mit Lambda-Ausdrücke.

Der zweite Unterschied ist, über das, was die eigentliche Funktion erlaubt ist, zu tun.

  • Eine Funktion mit 'def' definiert als jede Python-Code enthalten
  • Eine Funktion mit ‚Lambda‘ definiert auf einen Ausdruck zu bewerten hat, und kann somit nicht enthalten Aussagen wie Druck, Import, heben, ...

Beispiel:

def p(x): print x

funktioniert wie erwartet, während

lambda x: print x

ist ein Syntax.

Natürlich gibt es Workarounds - Ersatz print mit sys.stdout.write oder import mit __import__. Aber in der Regel sind Sie besser dran in diesem Fall mit einer Funktion gehen.

In diesem Interview Guido van Rossum, sagt er wünscht er nicht losgelassen hatte ‚Lambda 'in Python:

  

" Q. Welche Funktion von Python sind Sie am wenigsten zufrieden mit?
  Manchmal zu schnell Ich habe in der Annahme Beiträgen gewesen, und später erkennen, dass es ein Fehler war. Ein Beispiel würde einige der funktionalen Programmierung Features, wie Lambda-Funktionen sein. Lambda ist ein Schlüsselwort, das Sie erstellen eine kleine anonyme Funktion kann; integrierte Funktionen wie Karte, filtern und reduce eine Funktion über einen Sequenztyp, wie zum Beispiel eine Liste laufen.
  In der Praxis machte es, dass auch nicht heraus. Python hat nur zwei Bereiche: lokale und globale. Dies macht Lambda-Funktionen schmerzhaft zu schreiben, da Sie häufig den Zugriff auf Variablen im Rahmen wollen, wo das Lambda definiert wurde, aber man kann nicht wegen der beiden Bereiche. Es gibt einen Weg, um dieses, aber es ist etwas von einer Flickschusterei. Oft scheint es viel einfacher, in Python nur einen statt um von Messing mit Lambda-Funktionen für Schleife zu verwenden. Karte und Freunde arbeiten nur gut, wenn es bereits eine eingebaute Funktion, das tut, was Sie wollen.

IMHO kann Iambdas bequem sein, manchmal, aber ist in der Regel bequem auf Kosten der Lesbarkeit. Können Sie 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 es dauerte eine Minute, um es herauszufinden. Dies ist von Project Euler - ich werde nicht, das Problem, sagen, weil ich Spoiler hassen, aber es läuft in 0,124 Sekunden:)

Für n = 1000 hier einige timeit des von einer Funktion vs einem Lambda-Aufruf:

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

ich mit nosklo Rat einig: Wenn Sie die Funktion einen Namen geben müssen, verwenden Sie def. Ich behalte lambda Funktionen für Fälle, in denen ich nur einen kurzen Ausschnitt des Codes an eine andere Funktion übergeben, z.

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

Performance:

eine Funktion mit lambda Erstellen ist etwas schneller , als es mit def zu schaffen. Der Unterschied ist auf def im Einheimischen Tabelle einen Namenseintrag zu erstellen. Die sich ergebende Funktion hat die gleiche Ausführungsgeschwindigkeit.


Ablesbarkeit:

Lambda-Funktionen sind etwas weniger lesbar für die meist Python-Benutzer, aber auch viel prägnante unter bestimmten Umständen. Betrachten wir die Umwandlung von der Verwendung nicht-funktionalen zu Funktionsablauf:

# 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 können, die lambda Version ist kürzer und „leichter“ in dem Sinne, dass Sie brauchen nur lambda v: auf die ursprüngliche nicht-funktionsfähige Version hinzufügen, um die funktionalen Version zu konvertieren. Es ist auch viel mehr prägnant. Aber denken Sie daran, wird eine Menge von Python-Benutzer von der Lambda-Syntax zu verwechseln, also was Sie verlieren in der Länge und reale Komplexität könnte wieder in Verwirrung von Kolleginnen und Programmierern gewonnen werden.


Einschränkungen:

  • lambda Funktionen können nur einmal verwendet werden, es sei denn zu einem Variablennamen zugeordnet.
  • lambda Funktionen Variablennamen zugewiesen haben keinen Vorteil gegenüber def Funktionen.
  • lambda Funktionen kann schwierig oder unmöglich zu Beize sein.
  • Name def Funktionen muß sorgfältig sein, einigermaßen anschaulich und einzigartig oder zumindest sonst ungenutzt in Umfang gewählt werden.

Konsistenz:

Python vermeidet meist funktionale Programmierung Konventionen für Verfahrens- und einfachere Ziel Semantik. Der lambda Betreiber steht in direktem Gegensatz zu dieser Voreingenommenheit. Darüber hinaus als Alternative zu den bereits vorherrschenden def, die lambda Funktion fügt Ihre Syntax Vielfalt. Einige würden bedenken, dass weniger konsistent.


Bereits bestehende Funktionen:

Wie bereits von anderen erwähnt, können viele Verwendungen von lambda auf dem Gebiet von Mitgliedern der operator oder anderen Module ersetzt werden. Zum Beispiel:

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

, um die bereits bestehende Funktion kann Code besser lesbar in vielen Fällen machen.


Das Pythonic Prinzip: „Es sollte ein und vorzugsweise nur eine offensichtliche Art und Weise, es zu tun“

Das ist ähnlich wie bei der einzige Quelle der Wahrheit Lehre. Leider hat dich das einzigen offensichtliche-Wege-to-do-it-Prinzip immer ein wehmütiges Streben nach Python, anstatt eine wahre Leitprinzip mehr gewesen. Betrachten wir die sehr mächtige Array Comprehensions in Python. Sie sind funktional äquivalent zu den map und filter Funktionen:

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

lambda und def gleich sind.

Es ist eine Sache der Meinung, aber ich würde sagen, dass alles, was in der Sprache Python für den allgemeinen Gebrauch bestimmt ist, die offensichtlich nichts brechen „Pythonic“ genug.

Während mit den anderen Antworten zustimmen, manchmal ist es besser lesbar. Hier ist ein Beispiel, wo lambda praktisch ist, in einem Anwendungsfall mir Begegnung immer ein N-dimensionalen defaultdict
Hier ein Beispiel:

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

Ich finde es besser lesbar als eine def für die zweite Dimension zu schaffen. Dies ist noch wichtiger für höhere Dimensionen.

Die primäre Verwendung von Lambda hat für einfache Callback-Funktionen immer und für Karte, reduziert, Filter, die eine Funktion als Argument erfordern. Mit Listenkomprehensionen zur Norm, und das addierte erlaubt, wenn, wie in:

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

es ist schwer, einen echten Fall für die Verwendung von Lambda im täglichen Gebrauch vorzustellen. Als Ergebnis würde ich sagen, Lambda vermeiden und verschachtelte Funktionen erstellen.

Eine wichtige Einschränkung von Lambdas ist, dass sie nichts außer einem Ausdruck enthalten. Es ist fast unmöglich für einen Lambda-Ausdruck etwas anderes als trivial Nebenwirkungen zu erzeugen, da es nicht annähernd so reich einen Körper als def'ed Funktion haben kann.

Dass gesagt wird, beeinflusste Lua meinen Programmierstil zu der umfangreichen Verwendung von anonymen Funktionen, und ich Wurf mit ihnen meinen Code. Hinzu kommt, dass, neige ich dazu, über Karte / reduzieren als abstrakte Operatoren in einer Weise, denke ich, nicht Listenkomprehensionen oder Generatoren betrachten, fast so, als wenn ich eine Implementierung Entscheidung ausdrücklich bin aufzuschieben, indem diese Operatoren.

Edit:. Dies ist eine ziemlich alte Frage, und meine Meinung zu diesem Thema haben sich geändert, etwas

Zunächst einmal bin ich stark voreingenommen gegen einen lambda Ausdruck einer Variablen zugewiesen; wie Python hat eine spezielle Syntax nur für diesen (Hinweis, def). Zusätzlich zu, dass auch viele der Anwendungen für Lambda, wenn sie nicht bekommen, einen Namen ist bereits vordefiniert (und effizient) Implementierungen. Zum Beispiel kann das Beispiel in Frage abgekürzt werden, um nur (1).__add__, ohne dass es in einem lambda oder def wickeln. Viele andere gängige Anwendungen können mit einer Kombination der operator, itertools und functools Module erfüllt werden.

  

Mehr bevorzugt: Lambda-Funktionen oder verschachtelte Funktionen (def)

Es ist ein Vorteil, eine Lambda über eine reguläre Funktion zu verwenden (sie in einem Ausdruck erzeugt werden), und mehr Nachteile. Aus diesem Grund ziehe ich es Funktionen mit dem def Schlüsselwort zu erstellen, anstatt mit Lambda-Ausdrücke.

Erster Punkt - sie sind die gleiche Art von Objekt

Ein Lambda-Ergebnisse in der gleichen Art von Objekt als normale 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, sie sind Objekte erster Klasse.

Sowohl lambdas und Funktionen:

  • kann als Argument übergeben herum (gleiche wie eine reguläre Funktion)
  • , wenn innerhalb einer äußeren Funktion werden einen Verschluss über, die äußeren Funktionen Einheimischen erstellt

Aber lambdas werden, standardmäßig fehlen einige Dinge, die Funktionen erhalten über volle Funktionsdefinition Syntax.

Ein lamba des __name__ ist '<lambda>'

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

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

So Lambda ist nicht programmatisch in ihrem Namensraum nachgeschlagen werden.

Dies schränkt bestimmte Dinge. Zum Beispiel kann foo mit serialisierten Code nachgeschlagen werden, während l kann 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 Nachschlag nur in Ordnung - weil es seinen eigenen Namen kennt:

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

Lambdas haben keine Anmerkungen und kein docstring

Grundsätzlich lambdas sind nicht dokumentiert. Lassen Sie uns umschreiben foo werden besser dokumentiert:

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

Nun foo hat Dokumentation:

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

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

Während wir haben den gleichen Mechanismus nicht den gleichen Informationen lambda zu geben:

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

<lambda> lambda (...)

Aber wir können sie hacken auf:

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

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

Aber es gibt wahrscheinlich einige Fehler die Ausgabe von Hilfe vermasselt, though.

Lambdas können nur einen Ausdruck zurückgeben

Lambdas können nicht komplexe Anweisungen geben, nur Ausdrücke.

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

Ausdrücke können zugegebenermaßen recht komplex sein, und wenn Sie versuchen sehr hart können Sie wahrscheinlich das gleiche mit einem Lambda erreichen, aber die zusätzliche Komplexität ist eher ein Nachteil freien Code zu schreiben.

Wir verwenden Python für Klarheit und Wartbarkeit. Übermäßiger Einsatz von Lambda-Ausdrücke kann dagegen arbeiten.

Das nur Kopf für Lambda-Ausdrücke: kann in einem einzigen Ausdruck erstellt werden

Dies ist nur möglich, den Kopf. Da Sie eine Lambda mit einem Ausdruck erstellen können, können Sie es innerhalb eines Funktionsaufrufs erstellen.

Erstellen eine Funktion in einem Funktionsaufruf vermeidet die (preiswert)-Namen-Suche im Vergleich zu einem an anderer Stelle geschaffen.

Da jedoch Python streng ausgewertet wird, gibt es keinen anderen Performance-Gewinn von Vermeidung der Namen-Suche so beiseite zu tun.

Für einen sehr einfachen Ausdruck, könnte ich einen Lambda wählen.

Ich neige dazu, auch Lambda-Ausdrücke zu verwenden, wenn interaktiven Python zu tun, mehrere Zeilen zu vermeiden, wenn man tun wird. Ich verwende die folgende Art von Code-Format, wenn ich in einem Argumente zu einem Konstruktor übergeben werden soll, wenn timeit.repeat Aufruf:

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 jetzt:

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

Ich glaube, die geringe Zeitdifferenz oben kann auf die Namenssuche in return_nullary_function zugeschrieben werden. - beachten Sie, dass es sehr vernachlässigbar

Fazit

Lambdas sind gut für informelle Situationen, in denen Sie Codezeilen zugunsten minimieren wollen einen singulären Punkt zu machen.

Lambdas sind schlecht für mehr formalen Situationen, in denen Sie Klarheit für die Redaktion von Code benötigen, die später kommen, vor allem in Fällen, in denen sie nicht trivial.

Wir wissen, wir sollen unseren Objekten gute Namen geben. Wie können wir tun, wenn das Objekt nicht Name?

Aus all diesen Gründen ziehe ich mit def Funktionen zu erstellen, anstatt mit lambda.

  • Rechenzeit.
  • Funktion ohne Namen.
  • Eine Funktion und viele nutzen Funktionalität zu erreichen.

Unter Berücksichtigung eines 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

Wenn Sie nur gehen, um das Lambda auf eine Variable im lokalen Bereich zuzuordnen, auch Sie können auf jeden Fall verwenden, da es besser lesbar ist und kann in Zukunft leicht erweitert, um mehr werden:

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)

Eine Verwendung für lambdas ich gefunden habe, ... ist in Debug-Meldungen.

Da lambdas kann lazily ausgewertet werden Sie Code wie folgt haben:

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

anstelle von möglicherweise teuer:

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

, die den Format-String verarbeitet, auch wenn der Debug-Aufruf nicht ausgegeben, weil die aktuellen Protokollstufe erzeugt.

Natürlich für sie das Protokollierungsmodul im Einsatz, wie beschrieben arbeiten müssen lambdas als „lazy Parameter“ unterstützen (wie mein Logging-Modul der Fall ist).

Die gleiche Idee kann auf Nachfrage Inhalt Wertschöpfung zu jedem anderen Fall von lazy evaluation angewandt werden.

Zum Beispiel dieses benutzerdefinierten ternären 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)

statt:

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

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

mit Lambda-Ausdrücke nur der Ausdruck von der Bedingung ausgewählt wird ausgewertet werden, ohne Lambda-Ausdrücke werden beide ausgewertet werden.

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

Ich bin mit nosklo. By the way, auch mit einem einmal verwenden, wegzuwerfen Funktion, die meiste Zeit wollen Sie nur etwas von dem Bedienmodul verwenden.

z:

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

Sie möchten eine Funktion zu übergeben, die zwei Elemente hinzuzufügen.

Mit Lambda:

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

Die pythonic Art und Weise:

import operator
myFunction(data, operator.add)

oder natürlich ist dies ein einfaches Beispiel, aber es gibt eine Menge Sachen, die Bedienmodul, einschließlich der Artikel Setter / Getter für Liste und dict bietet. Wirklich cool.

Lambda ist nützlich für die Erzeugung neuer Funktionen:

def somefunc(x): return lambda y: x+y
f = somefunc(10)
f(2)
>>> 12
f(4)
>>> 14

Ein wesentlicher Unterschied besteht darin, dass Sie keine Inline-def Funktionen nutzen zu können, was meiner Meinung nach ist der bequemste Anwendungsfall für eine lambda Funktion. Zum Beispiel beim Sortieren eine Liste von Objekten:

my_list.sort(key=lambda o: o.x)

Ich würde daher vorschlagen, die Verwendung von Lambda-Ausdrücke auf diese Art von trivialen Operationen zu halten, die auch von der automatischen Dokumentation durch die Benennung der Funktion zur Verfügung gestellt nicht wirklich profitieren.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top