Bevorzugter: Lambda-Funktionen oder verschachtelte Funktionen ( def
)?
Die Verwendung eines Lambda gegenüber einer regulären Funktion hat einen Vorteil: Sie werden in einem Ausdruck erstellt.
Es gibt mehrere Nachteile:
- kein Name (nur
'<lambda>'
)
- keine docstrings
- Keine Anmerkungen
- keine komplexen Aussagen
Sie sind auch beide der gleiche Objekttyp. Aus diesen Gründen ziehe ich es im Allgemeinen vor, Funktionen mit dem def
Schlüsselwort anstatt mit Lambdas zu erstellen .
Erster Punkt - sie sind die gleiche Art von Objekt
Ein Lambda ergibt den gleichen 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 weitergegeben werden (wie eine reguläre Funktion)
- Wenn sie innerhalb einer äußeren Funktion erstellt werden, werden sie zu einem Abschluss über den Einheimischen dieser äußeren Funktionen
In Lambdas fehlen jedoch standardmäßig einige Dinge, die Funktionen über die vollständige Syntax der Funktionsdefinition erhalten.
Eine Lamba __name__
ist'<lambda>'
Lambdas sind schließlich anonyme Funktionen, daher kennen sie ihren eigenen Namen nicht.
>>> l.__name__
'<lambda>'
>>> foo.__name__
'foo'
Daher können Lambdas nicht programmgesteuert in ihrem Namespace nachgeschlagen werden.
Dies schränkt bestimmte Dinge ein. Zum Beispiel foo
kann 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 Dokumentzeichenfolge
Grundsätzlich sind Lambdas nicht dokumentiert. Lassen Sie uns umschreiben foo
, um besser dokumentiert zu werden:
def foo() -> int:
"""a nullary function, returns 0 every time"""
return 0
Jetzt hat foo 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 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
Aber es gibt wahrscheinlich einen Fehler, der die Ausgabe der Hilfe durcheinander bringt.
Lambdas kann nur einen Ausdruck zurückgeben
Lambdas kann keine komplexen Anweisungen zurückgeben, sondern nur Ausdrücke.
>>> lambda: if True: 0
File "<stdin>", line 1
lambda: if True: 0
^
SyntaxError: invalid syntax
Ausdrücke können zugegebenermaßen ziemlich komplex sein, und wenn Sie sich sehr anstrengen, können Sie dies wahrscheinlich auch mit einem Lambda erreichen, aber die zusätzliche Komplexität wirkt sich eher nachteilig auf das Schreiben von klarem Code aus.
Wir verwenden Python für Klarheit und Wartbarkeit. Übermäßiger Gebrauch von Lambdas kann dem entgegenwirken.
Der einzige Vorteil für Lambdas: Kann in einem einzigen Ausdruck erstellt werden
Dies ist der einzig mögliche Vorteil. 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 vermieden.
Da Python jedoch streng evaluiert wird, gibt es keinen anderen Leistungsgewinn als die Vermeidung der Namenssuche.
Für einen sehr einfachen Ausdruck könnte ich ein Lambda wählen.
Ich neige auch dazu, Lambdas zu verwenden, wenn ich interaktives Python mache, um mehrere Zeilen zu vermeiden, wenn eine dies tut. Ich verwende das folgende Codeformat, wenn ich beim Aufruf ein Argument an einen Konstruktor übergeben möchte timeit.repeat
:
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, der geringfügige Zeitunterschied oben kann auf die Namenssuche in zurückgeführt werden return_nullary_function
- beachten Sie, dass er sehr vernachlässigbar ist.
Fazit
Lambdas eignen sich für informelle Situationen, in denen Sie Codezeilen minimieren möchten, um einen einzelnen Punkt hervorzuheben.
Lambdas sind schlecht für formellere Situationen, in denen Sie Klarheit für Code-Redakteure benötigen, die später kommen, insbesondere in Fällen, in denen sie nicht trivial sind.
Wir wissen, dass wir unseren Objekten gute Namen geben sollen. Wie können wir das tun, wenn das Objekt keinen Namen hat?
Aus all diesen Gründen ziehe ich es im Allgemeinen vor, Funktionen mit def
statt mit zu erstellen lambda
.
lambda
einverstanden, wann sie verwendet werden soll , aber ich bin nicht der Meinung, dass dies "sehr selten" ist, es ist üblich für Schlüsselfunktionensorted
oderitertools.groupby
usw., z. B.sorted(['a1', 'b0'], key= lambda x: int(x[1]))