Frage Was ist besser in Python zu verwenden: Lambda-Funktionen oder verschachtelte Funktionen ('def')?


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

Hier sind einige triviale Beispiele, wo sie funktional dasselbe tun, wenn sie in einer anderen Funktion gefunden werden:

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 den anderen? (Leistung? Lesbarkeit? Einschränkungen? Konsistenz? Usw.)

Ist es überhaupt wichtig? Wenn das nicht der Fall ist, verletzt das das Pythonic-Prinzip:

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


75
2017-09-25 17:15


Ursprung


Antworten:


Wenn Sie den lambda zu einem Namen, benutze a def stattdessen. defs sind nur syntaktische Zucker für eine Aufgabe, so dass das Ergebnis das gleiche ist, und sie sind viel flexibler und lesbarer.

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

Dieser Anwendungsfall ist jedoch sehr selten. Sie müssen selten nicht benannte Funktionsobjekte weitergeben.

Die Builtins map() und filter() brauche Funktionsobjekte, aber list comprehensions und Generatorausdrücke sind in der Regel besser lesbar als diese Funktionen und können alle Anwendungsfälle abdecken, ohne dass Lambdas benötigt werden.

Für die Fälle, in denen Sie wirklich ein kleines Funktionsobjekt benötigen, sollten Sie das verwenden operator Modulfunktionen, wie operator.add Anstatt von lambda x, y: x + y

Wenn du noch welche brauchst lambda nicht abgedeckt, könnten Sie darüber nachdenken, ein defum lesbarer zu sein. Wenn die Funktion komplexer ist als die bei operator Modul, a def ist wahrscheinlich besser.

Also, die reale Welt ist gut lambda Anwendungsfälle sind sehr selten.


84
2017-09-25 17:16



In der Praxis gibt es für mich zwei Unterschiede:

Der erste betrifft das, 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 keinen 'Namen' im lokalen Namespace erstellt.

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

In einigen Frameworks ist dies tatsächlich ziemlich häufig; zum Beispiel benutze ich Verdrehte viel, und so etwas zu tun

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

ist ziemlich üblich und prägnanter mit Lambdas.

Der zweite Unterschied besteht darin, 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 kann daher keine Anweisungen wie Drucken, Importieren, Erhöhen, ... enthalten.

Beispielsweise,

def p(x): print x

funktioniert wie erwartet, während

lambda x: print x

ist ein SyntaxError.

Natürlich gibt es Workarounds - Ersatz print mit sys.stdout.write, oder import mit __import__. Aber normalerweise ist es besser, in diesem Fall mit einer Funktion zu arbeiten.


25
2017-09-26 10:20



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

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

  Manchmal akzeptierte ich Beiträge zu schnell und erkannte später, dass es ein Fehler war. Ein Beispiel wären einige der funktionalen Programmierfunktionen wie Lambda-Funktionen. Lambda ist ein Schlüsselwort, mit dem Sie eine kleine anonyme Funktion erstellen können. Integrierte Funktionen wie map, filter und reduce führen eine Funktion über einen Sequenztyp wie eine Liste aus.

  In der Praxis lief es nicht so gut. Python hat nur zwei Bereiche: lokal und global. Dies macht das Schreiben von Lambda-Funktionen schmerzhaft, weil Sie häufig auf Variablen in dem Bereich zugreifen möchten, in dem das Lambda definiert wurde, dies jedoch aufgrund der beiden Gültigkeitsbereiche nicht möglich ist. Es gibt einen Weg, aber es ist eine Art Klotz. Oft scheint es in Python einfacher zu sein, einfach eine for-Schleife zu verwenden, anstatt mit Lambda-Funktionen herumzualbern. Karte und Freunde funktionieren nur dann gut, wenn es bereits eine eingebaute Funktion gibt, die das tut, was Sie wollen.

IMHO, Iambdas kann manchmal bequem sein, aber in der Regel bequem auf Kosten der Lesbarkeit. Kannst du mir sagen, was das bedeutet:

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

Ich schrieb es und es dauerte eine Minute, bis ich es herausgefunden hatte. Dies ist von Project Euler - ich werde nicht sagen, welches Problem, weil ich Spoiler hasse, aber es läuft in 0,124 Sekunden :)


17
2017-09-25 17:29



Für n = 1000 gibt es hier eine Zeit, eine Funktion gegen ein Lambda zu benennen:

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
2017-08-16 13:16



Ich stimme dem Rat 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 ein kurzes Code-Snippet an eine andere Funktion übergebe, z. B .:

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

6
2017-09-25 17:19



Performance:

Erstellen einer Funktion mit lambda ist etwas schneller als mit ihm zu erstellen def. Der Unterschied ist auf def Erstellen eines Namenseintrags in der lokalen Tabelle. Die resultierende Funktion hat die gleiche Ausführungsgeschwindigkeit.


Lesbarkeit:

Lambda-Funktionen sind für die meisten Python-Benutzer etwas weniger lesbar, unter Umständen aber auch deutlich prägnanter. Erwägen Sie, von der Verwendung nicht-funktionaler in funktionale Routine umzustellen:

# 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 "einfacher" in dem Sinne, dass Sie nur hinzufügen müssen lambda v: zur ursprünglichen nicht-funktionalen Version, um in die funktionale Version zu konvertieren. Es ist auch viel prägnanter. Aber denken Sie daran, dass viele Python-Benutzer durch die Lambda-Syntax verwirrt werden. Was Sie an Länge und tatsächlicher Komplexität verlieren, könnte daher in Verwirrung von anderen Programmierern zurückgewonnen werden.


Einschränkungen:

  • lambda Funktionen können nur einmal verwendet werden, es sei denn, sie sind einem Variablennamen zugewiesen.
  • lambda Funktionen, die Variablennamen zugewiesen sind, haben keinen Vorteil gegenüber def Funktionen.
  • lambda Funktionen können schwierig oder unmöglich zu beizen sein.
  • def Die Namen der Funktionen müssen sorgfältig ausgewählt werden, um angemessen beschreibend und einzigartig oder zumindest in sonstiger Weise ungenutzt zu sein.

Konsistenz:

Python vermeidet hauptsächlich funktionale Programmierkonventionen zugunsten prozeduraler und einfacher objektiver Semantiken. Das lambdaBetreiber steht in direktem Gegensatz zu dieser Voreingenommenheit. Darüber hinaus als Alternative zu den bereits vorherrschenden def, das lambda Funktion fügt Ihrer Syntax Vielfalt hinzu. Manche würden das als weniger konsistent betrachten.


Vorhandene Funktionen:

Wie von anderen bemerkt, viele Anwendungen von lambda im Feld kann durch Mitglieder der ersetzt werden operator oder andere Module. Zum Beispiel:

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

Die Verwendung der bereits vorhandenen Funktion kann den Code in vielen Fällen besser lesbar machen.


Das pythonische Prinzip: "Es sollte einen - und vorzugsweise nur einen offensichtlichen Weg geben, es zu tun"

Das ist ähnlich wie bei der einzige Quelle der Wahrheit Lehre. Unglücklicherweise war das Prinzip, dass es sich nur um eine naheliegende Methode handelt, für Python immer eher ein wehmütiges Streben als ein wahres Leitprinzip. Betrachten Sie die sehr mächtigen Array-Entdeckungen in Python. Sie sind funktionell äquivalent zu map und filter Funktionen:

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

lambda und def sind gleich.

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


5
2018-02-11 18:14



Die primäre Verwendung von Lambda war immer für einfache Callback-Funktionen und für Map, Reduce, Filter, die eine Funktion als Argument erfordern. Mit List Comprehensions wird die Norm, und das Hinzufügen erlaubt, wenn in:

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

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


4
2017-09-25 19:13



Eine wichtige Einschränkung von Lambdas ist, dass sie außer einem Ausdruck nichts enthalten können. Es ist nahezu unmöglich, dass ein Lambda-Ausdruck neben trivialen Nebenwirkungen auch nur annähernd so reich ist wie ein Körper defFunktion.

Davon abgesehen hat Lua meinen Programmierstil auf den extensiven Gebrauch von anonymen Funktionen beeinflusst, und ich habe meinen Code mit ihnen verwechselt. Außerdem tendiere ich dazu, map / reduce als abstrakte Operatoren in einer Weise zu betrachten, die ich nicht als List Comprehensions oder Generatoren betrachte, fast so, als würde ich eine Implementierungsentscheidung explizit mit diesen Operatoren verzögern.

Bearbeiten: Das ist eine ziemlich alte Frage, und meine Meinungen zu diesem Thema haben sich etwas geändert.

Zunächst einmal bin ich stark voreingenommen gegen eine Zuordnung von lambda Ausdruck zu einer Variablen; wie Python hat eine spezielle Syntax nur dafür (Hinweis, def). Darüber hinaus haben viele der Anwendungen für Lambda, auch wenn sie keinen Namen erhalten, vordefinierte (und effizientere) Implementierungen. Zum Beispiel kann das betreffende Beispiel abgekürzt werden (1).__add__, ohne die Notwendigkeit, es in ein lambda oder def. Viele andere häufige Anwendungen können mit einer Kombination der beiden erfüllt werden operator, itertools und functools Module.


3
2018-06-17 00:02



Obwohl es mit den anderen Antworten übereinstimmt, ist es manchmal besser lesbar. Hier ist ein Beispiel wo lambda praktisch, in einem Anwendungsfall begegne ich immer einem N dimensionalen defaultdict.
Hier ist ein Beispiel:

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

Ich finde es lesbarer als ein deffür die zweite Dimension. Dies ist für höhere Dimensionen noch bedeutender.


3
2017-12-27 10:54



Eine Verwendung für Lambdas, die ich gefunden habe ... ist in Debug-Nachrichten.

Da Lambdas träge ausgewertet werden können, können Sie folgenden Code haben:

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

statt möglicherweise teuer:

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

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

Damit es wie beschrieben funktioniert, muss das verwendete Logging-Modul lambdas als "lazy parameters" (wie mein Logging-Modul) unterstützen.

Die gleiche Idee kann auf jeden anderen Fall einer faulen Bewertung für die Erzeugung von Inhalten 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 von:

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 Lambda werden beide ausgewertet.

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


2
2017-10-21 21:37