Das kannst du also nicht machen. Verwenden Sie normale Funktionen.
DrTyrsa
1
Was bringt es, einer anonymen Funktion einen Namen zu geben?
John La Rooy
2
@gnibbler Sie können den Namen verwenden, um auf die Funktion zu verweisen. y () ist in der REPL einfacher zu verwenden als (lambda: 0) ().
Thomas Jung
Also , was ist der Vorteil von y=lambda...über def y:dann?
John La Rooy
@gnibbler Irgendein Kontext: Ich wollte eine Funktion def g (f, e) definieren, die im glücklichen Fall f und e aufruft, wenn ein Fehler erkannt wurde. Abhängig vom Szenario kann e eine Ausnahme auslösen oder einen gültigen Wert zurückgeben. Um g zu verwenden, wollte ich g (Lambda x: x * 2, Lambda e: erhöhen e) oder alternativ g (Lambda x: x * 2, Lambda e: 0) schreiben.
Thomas Jung
Antworten:
169
Es gibt mehrere Möglichkeiten, einen Python zu häuten:
y =lambda:(_ for _ in()).throw(Exception('foobar'))
Lambdas akzeptieren Aussagen. Da raise exes sich um eine Aussage handelt, können Sie einen Allzweck-Raiser schreiben:
def raise_(ex):raise ex
y =lambda: raise_(Exception('foobar'))
Aber wenn Ihr Ziel darin besteht, a zu vermeiden def, schneidet dies offensichtlich nicht. Sie können jedoch Ausnahmen bedingt auslösen, z.
y =lambda x:2*x if x <10else raise_(Exception('foobar'))
Alternativ können Sie eine Ausnahme auslösen, ohne eine benannte Funktion zu definieren. Alles was Sie brauchen ist ein starker Magen (und 2.x für den angegebenen Code):
Vielen Dank an @WarrenSpencer für den Hinweis auf eine sehr einfache Antwort, wenn es Ihnen egal ist, welche Ausnahme ausgelöst wird : y = lambda: 1/0.
Wenn es Ihnen egal ist, welche Art von Ausnahme ausgelöst wird, funktioniert auch Folgendes : lambda: 1 / 0. Am Ende wird nur ein ZeroDivisionError anstelle einer regulären Ausnahme ausgelöst. Denken Sie daran, dass es für jemanden, der Ihren Code debuggt, seltsam aussehen kann, wenn sich die Ausnahme ausbreiten darf, um eine Reihe von ZeroDivisionErrors zu sehen.
Warren Spencer
Tolle Lösung @WarrenSpencer. Der meiste Code weist nicht viele Nullteilungsfehler auf, daher ist er so charakteristisch, als könnten Sie den Typ selbst auswählen.
JWG
2
y = 1/0ist eine super kluge Lösung, wenn der Ausnahmetyp irrelevant ist
Saher Ahwal
3
Kann uns jemand erklären, was in den Lösungen für „dunkle Kunst / starker Magen“ tatsächlich vor sich geht?
Es ist ziemlich hackig, aber für das Schreiben von Tests, bei denen Sie Funktionen verspotten möchten, funktioniert dies ordentlich !!!
Kannan Ekanath
8
Funktioniert, aber Sie sollten es nicht tun.
August
1
Das funktioniert bei mir nicht, ich bekomme eine SyntaxErrorauf Python 2.7.11.
Nick Sweeting
Ich erhalte auch den obigen Fehler (SyntaxError) auf Python 2.7.5
Dinesh
1
Dies ist spezifisch für Python 3, aber ich glaube nicht, dass Python 2 dies zulässt.
Saher Ahwal
16
Eigentlich gibt es einen Weg, aber er ist sehr erfunden.
Mit der compile()integrierten Funktion können Sie ein Codeobjekt erstellen . Auf diese Weise können Sie die raiseAnweisung (oder eine andere Anweisung) verwenden, dies wirft jedoch eine weitere Herausforderung auf: die Ausführung des Codeobjekts. Der übliche Weg wäre, die execAnweisung zu verwenden, aber das führt Sie zurück zum ursprünglichen Problem, nämlich dass Sie keine Anweisungen in einem lambda(oder einem eval()) ausführen können .
Die Lösung ist ein Hack. Callables wie das Ergebnis einer lambdaAnweisung haben alle ein Attribut __code__, das tatsächlich ersetzt werden kann. Wenn Sie also ein aufrufbares __code__Objekt erstellen und dessen Wert durch das Codeobjekt von oben ersetzen , erhalten Sie etwas, das ohne Verwendung von Anweisungen ausgewertet werden kann. Das Erreichen all dessen führt jedoch zu einem sehr undurchsichtigen Code:
map(lambda x, y, z: x.__setattr__(y, z) or x, [lambda: 0], ["__code__"], [compile("raise Exception", "", "single"])[0]()
Das Obige bewirkt Folgendes:
Der compile()Aufruf erstellt ein Codeobjekt, das die Ausnahme auslöst.
Das lambda: 0gibt einen Aufruf zurück, der nichts anderes tut, als den Wert 0 zurückzugeben. Dies wird verwendet, um das obige Codeobjekt später auszuführen.
Das lambda x, y, zerstellt eine Funktion, die die __setattr__Methode des ersten Arguments mit den verbleibenden Argumenten aufruft und das erste Argument zurückgibt! Dies ist notwendig, weil es __setattr__selbst zurückkehrt None;
Der map()Aufruf nimmt das Ergebnis von lambda: 0und lambda x, y, zersetzt das __code__Objekt durch das Ergebnis des compile()Aufrufs. Das Ergebnis dieser Kartenoperation ist eine Liste mit einem Eintrag, der von zurückgegeben lambda x, y, zwird. Deshalb benötigen wir diesen lambda: Wenn wir ihn __setattr__sofort verwenden würden, würden wir den Verweis auf das lambda: 0Objekt verlieren !
Schließlich wird das erste (und einzige) Element der vom map()Aufruf zurückgegebenen Liste ausgeführt, was dazu führt, dass das Codeobjekt aufgerufen wird, wodurch letztendlich die gewünschte Ausnahme ausgelöst wird.
Es funktioniert (getestet in Python 2.6), aber es ist definitiv nicht schön.
Ein letzter Hinweis: Wenn Sie Zugriff auf das typesModul haben (für das die importAnweisung vor Ihrem verwendet werden müsste eval), können Sie diesen Code etwas verkürzen: Mit können types.FunctionType()Sie eine Funktion erstellen, die das angegebene Codeobjekt ausführt, sodass Sie gewonnen haben Es ist nicht nötig, eine Dummy-Funktion zu erstellen lambda: 0und den Wert ihres __code__Attributs zu ersetzen .
Dies ist wahr, aber es beantwortet die Frage nicht wirklich; Das Auslösen einer Ausnahme von einem Lambda-Ausdruck ist einfach (und das Abfangen kann manchmal mithilfe sehr ausgeklügelter Tricks erreicht werden, siehe stackoverflow.com/a/50916686/2560053 oder stackoverflow.com/a/50961836/2560053 ).
Thomas Baruchel
15
Wenn Sie nur einen Lambda-Ausdruck wünschen, der eine beliebige Ausnahme auslöst, können Sie dies mit einem unzulässigen Ausdruck erreichen. Es wird beispielsweise lambda x: [][0]versucht, auf das erste Element in einer leeren Liste zuzugreifen, wodurch ein IndexError ausgelöst wird.
BITTE BEACHTEN SIE : Dies ist ein Hack, keine Funktion. Sie nicht verwenden , diese in jedem (nicht Code-Golf) Code , dass ein anderer Mensch sehen oder verwenden können.
In meinem Fall bekomme ich : TypeError: <lambda>() takes exactly 1 positional argument (2 given). Sind Sie sich des IndexError sicher?
Jovik
4
Ja. Haben Sie vielleicht die falsche Anzahl von Argumenten angegeben? Wenn Sie eine Lambda-Funktion benötigen, die eine beliebige Anzahl von Argumenten annehmen kann, verwenden Sie lambda *x: [][0]. (Die Originalversion verwendet nur ein Argument; für keine Argumente verwenden lambda : [][0]; für zwei verwenden lambda x,y: [][0]; usw.)
Kyle Strand
3
Ich habe dies ein wenig erweitert: lambda x: {}["I want to show this message. Called with: %s" % x] Produziert: KeyError: 'I want to show this message. Called with: foo'
ErlVolton
@ErlVolton Clever! Obwohl es eine schreckliche Idee ist, dies überall außer in einem einmaligen Skript zu verwenden ...
Kyle Strand
Ich verwende vorübergehend Unit-Tests für ein Projekt, bei dem ich mich nicht darum gekümmert habe, meinen Logger wirklich zu verspotten. Es wird ausgelöst, wenn Sie versuchen, einen Fehler oder einen kritischen Fehler zu protokollieren. Also ... ja schrecklich, obwohl einvernehmlich :)
ErlVolton
10
Ich möchte das UPDATE 3 der Antwort von Marcelo Cantos erläutern :
Wir rufen den Konstruktor des Codeobjekts mit den folgenden Argumenten auf:
argcount = 1
kwonlyargcount = 0
nlocals = 1
Stapelgröße = 1
Flags = 67
codestring = b '| \ 0 \ 202 \ 1 \ 0'
Konstanten = ()
names = ()
varnames = ('x',)
Dateiname = ''
name = ''
firstlineno = 1
lnotab = b ''
Informationen zur Bedeutung der Argumente finden Sie in der Definition von PyCodeObjecthttps://github.com/python/cpython/blob/master/Include/code.h . Der Wert 67 für das flagsArgument ist beispielsweiseCO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE .
Das wichtigste Argument ist das, codestringdas Anweisungs-Opcodes enthält. Mal sehen, was sie bedeuten.
LOAD_FAST(var_num)Pushes a reference to the local co_varnames[var_num] onto the stack.
Also schieben wir den Verweis auf xauf den Stapel. Das varnamesist eine Liste von Zeichenfolgen, die nur 'x' enthalten. Wir werden das einzige Argument der Funktion, die wir definieren, auf den Stapel übertragen.
Das nächste Byte ist der Opcode für RAISE_VARARGSund das nächste Byte ist sein Argument, dh 1.
RAISE_VARARGS(argc)Raises an exception using one of the 3 forms of the raise statement, depending on the value of argc:0:raise(re-raise previous exception)1:raise TOS (raise exception instance or type at TOS)2:raise TOS1 from TOS (raise exception instance or type at TOS1 with __cause__ set to TOS)
Die AGB sind die Top-of-Stack. Da wir das erste Argument ( x) unserer Funktion in den Stapel verschoben haben und argc1 sind, werden wir das auslösen,
xwenn es sich um eine Ausnahmeinstanz handelt, oder eine Instanz von xerstellen und es anderweitig auslösen.
Das letzte Byte, dh 0, wird nicht verwendet. Es ist kein gültiger Opcode. Es könnte genauso gut nicht da sein.
Rufen Sie die Hilfe für ein Funktionsobjekt auf, um zu sehen, was die Argumente bedeuten.
Help on class function in module builtins:class function(object)| function(code, globals, name=None, argdefs=None, closure=None)||Create a function object.|| code
| a code object
| globals
| the globals dictionary
| name
| a string that overrides the name from the code object
| argdefs
| a tuple that specifies the default argument values
| closure
| a tuple that supplies the bindings for free variables
Wir rufen dann die konstruierte Funktion auf, die eine Exception-Instanz als Argument übergibt. Folglich haben wir eine Lambda-Funktion aufgerufen, die eine Ausnahme auslöst. Lassen Sie uns das Snippet ausführen und sehen, dass es tatsächlich wie beabsichtigt funktioniert.
>>> type(lambda:0)(type((lambda:0).__code__)(...1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b''),{}...)(Exception())Traceback(most recent call last):File"<stdin>", line 3,in<module>File"", line 1,inException
Verbesserungen
Wir haben gesehen, dass das letzte Byte des Bytecodes nutzlos ist. Lassen Sie uns diesen komplizierten Ausdruck nicht nadeln. Lassen Sie uns dieses Byte entfernen. Auch wenn wir ein wenig Golf spielen möchten, können wir die Instanziierung von Exception weglassen und stattdessen die Exception-Klasse als Argument übergeben. Diese Änderungen würden zu folgendem Code führen:
y=lambda...
überdef y:
dann?Antworten:
Es gibt mehrere Möglichkeiten, einen Python zu häuten:
Lambdas akzeptieren Aussagen. Da
raise ex
es sich um eine Aussage handelt, können Sie einen Allzweck-Raiser schreiben:Aber wenn Ihr Ziel darin besteht, a zu vermeiden
def
, schneidet dies offensichtlich nicht. Sie können jedoch Ausnahmen bedingt auslösen, z.Alternativ können Sie eine Ausnahme auslösen, ohne eine benannte Funktion zu definieren. Alles was Sie brauchen ist ein starker Magen (und 2.x für den angegebenen Code):
Und eine starke Python3-Magenlösung :
Vielen Dank an @WarrenSpencer für den Hinweis auf eine sehr einfache Antwort, wenn es Ihnen egal ist, welche Ausnahme ausgelöst wird :
y = lambda: 1/0
.quelle
lambda: 1 / 0
. Am Ende wird nur ein ZeroDivisionError anstelle einer regulären Ausnahme ausgelöst. Denken Sie daran, dass es für jemanden, der Ihren Code debuggt, seltsam aussehen kann, wenn sich die Ausnahme ausbreiten darf, um eine Reihe von ZeroDivisionErrors zu sehen.y = 1/0
ist eine super kluge Lösung, wenn der Ausnahmetyp irrelevant istWie wäre es mit:
quelle
SyntaxError
auf Python 2.7.11.Eigentlich gibt es einen Weg, aber er ist sehr erfunden.
Mit der
compile()
integrierten Funktion können Sie ein Codeobjekt erstellen . Auf diese Weise können Sie dieraise
Anweisung (oder eine andere Anweisung) verwenden, dies wirft jedoch eine weitere Herausforderung auf: die Ausführung des Codeobjekts. Der übliche Weg wäre, dieexec
Anweisung zu verwenden, aber das führt Sie zurück zum ursprünglichen Problem, nämlich dass Sie keine Anweisungen in einemlambda
(oder einemeval()
) ausführen können .Die Lösung ist ein Hack. Callables wie das Ergebnis einer
lambda
Anweisung haben alle ein Attribut__code__
, das tatsächlich ersetzt werden kann. Wenn Sie also ein aufrufbares__code__
Objekt erstellen und dessen Wert durch das Codeobjekt von oben ersetzen , erhalten Sie etwas, das ohne Verwendung von Anweisungen ausgewertet werden kann. Das Erreichen all dessen führt jedoch zu einem sehr undurchsichtigen Code:map(lambda x, y, z: x.__setattr__(y, z) or x, [lambda: 0], ["__code__"], [compile("raise Exception", "", "single"])[0]()
Das Obige bewirkt Folgendes:
Der
compile()
Aufruf erstellt ein Codeobjekt, das die Ausnahme auslöst.Das
lambda: 0
gibt einen Aufruf zurück, der nichts anderes tut, als den Wert 0 zurückzugeben. Dies wird verwendet, um das obige Codeobjekt später auszuführen.Das
lambda x, y, z
erstellt eine Funktion, die die__setattr__
Methode des ersten Arguments mit den verbleibenden Argumenten aufruft und das erste Argument zurückgibt! Dies ist notwendig, weil es__setattr__
selbst zurückkehrtNone
;Der
map()
Aufruf nimmt das Ergebnis vonlambda: 0
undlambda x, y, z
ersetzt das__code__
Objekt durch das Ergebnis descompile()
Aufrufs. Das Ergebnis dieser Kartenoperation ist eine Liste mit einem Eintrag, der von zurückgegebenlambda x, y, z
wird. Deshalb benötigen wir diesenlambda
: Wenn wir ihn__setattr__
sofort verwenden würden, würden wir den Verweis auf daslambda: 0
Objekt verlieren !Schließlich wird das erste (und einzige) Element der vom
map()
Aufruf zurückgegebenen Liste ausgeführt, was dazu führt, dass das Codeobjekt aufgerufen wird, wodurch letztendlich die gewünschte Ausnahme ausgelöst wird.Es funktioniert (getestet in Python 2.6), aber es ist definitiv nicht schön.
Ein letzter Hinweis: Wenn Sie Zugriff auf das
types
Modul haben (für das dieimport
Anweisung vor Ihrem verwendet werden müssteeval
), können Sie diesen Code etwas verkürzen: Mit könnentypes.FunctionType()
Sie eine Funktion erstellen, die das angegebene Codeobjekt ausführt, sodass Sie gewonnen haben Es ist nicht nötig, eine Dummy-Funktion zu erstellenlambda: 0
und den Wert ihres__code__
Attributs zu ersetzen .quelle
Mit Lambda-Formularen erstellte Funktionen dürfen keine Anweisungen enthalten .
quelle
Wenn Sie nur einen Lambda-Ausdruck wünschen, der eine beliebige Ausnahme auslöst, können Sie dies mit einem unzulässigen Ausdruck erreichen. Es wird beispielsweise
lambda x: [][0]
versucht, auf das erste Element in einer leeren Liste zuzugreifen, wodurch ein IndexError ausgelöst wird.BITTE BEACHTEN SIE : Dies ist ein Hack, keine Funktion. Sie nicht verwenden , diese in jedem (nicht Code-Golf) Code , dass ein anderer Mensch sehen oder verwenden können.
quelle
TypeError: <lambda>() takes exactly 1 positional argument (2 given)
. Sind Sie sich des IndexError sicher?lambda *x: [][0]
. (Die Originalversion verwendet nur ein Argument; für keine Argumente verwendenlambda : [][0]
; für zwei verwendenlambda x,y: [][0]
; usw.)lambda x: {}["I want to show this message. Called with: %s" % x]
Produziert:KeyError: 'I want to show this message. Called with: foo'
Ich möchte das UPDATE 3 der Antwort von Marcelo Cantos erläutern :
Erläuterung
lambda: 0
ist eine Instanz derbuiltins.function
Klasse.type(lambda: 0)
ist diebuiltins.function
Klasse.(lambda: 0).__code__
ist eincode
Objekt.Ein
code
Objekt ist ein Objekt, das unter anderem den kompilierten Bytecode enthält. Es ist hier in CPython https://github.com/python/cpython/blob/master/Include/code.h definiert . Die Methoden werden hier implementiert: https://github.com/python/cpython/blob/master/Objects/codeobject.c . Wir können die Hilfe für das Codeobjekt ausführen:type((lambda: 0).__code__)
ist die Codeklasse.Also wenn wir sagen
Wir rufen den Konstruktor des Codeobjekts mit den folgenden Argumenten auf:
Informationen zur Bedeutung der Argumente finden Sie in der Definition von
PyCodeObject
https://github.com/python/cpython/blob/master/Include/code.h . Der Wert 67 für dasflags
Argument ist beispielsweiseCO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE
.Das wichtigste Argument ist das,
codestring
das Anweisungs-Opcodes enthält. Mal sehen, was sie bedeuten.Die Dokumentation der Opcodes finden Sie hier https://docs.python.org/3.8/library/dis.html#python-bytecode-instructions . Das erste Byte ist der Opcode für
LOAD_FAST
, das zweite Byte ist sein Argument, dh 0.Also schieben wir den Verweis auf
x
auf den Stapel. Dasvarnames
ist eine Liste von Zeichenfolgen, die nur 'x' enthalten. Wir werden das einzige Argument der Funktion, die wir definieren, auf den Stapel übertragen.Das nächste Byte ist der Opcode für
RAISE_VARARGS
und das nächste Byte ist sein Argument, dh 1.Die AGB sind die Top-of-Stack. Da wir das erste Argument (
x
) unserer Funktion in den Stapel verschoben haben undargc
1 sind, werden wir das auslösen,x
wenn es sich um eine Ausnahmeinstanz handelt, oder eine Instanz vonx
erstellen und es anderweitig auslösen.Das letzte Byte, dh 0, wird nicht verwendet. Es ist kein gültiger Opcode. Es könnte genauso gut nicht da sein.
Zurück zum Code-Snippet:
Wir haben den Konstruktor des Codeobjekts aufgerufen:
Wir übergeben das Codeobjekt und ein leeres Wörterbuch an den Konstruktor eines Funktionsobjekts:
Rufen Sie die Hilfe für ein Funktionsobjekt auf, um zu sehen, was die Argumente bedeuten.
Wir rufen dann die konstruierte Funktion auf, die eine Exception-Instanz als Argument übergibt. Folglich haben wir eine Lambda-Funktion aufgerufen, die eine Ausnahme auslöst. Lassen Sie uns das Snippet ausführen und sehen, dass es tatsächlich wie beabsichtigt funktioniert.
Verbesserungen
Wir haben gesehen, dass das letzte Byte des Bytecodes nutzlos ist. Lassen Sie uns diesen komplizierten Ausdruck nicht nadeln. Lassen Sie uns dieses Byte entfernen. Auch wenn wir ein wenig Golf spielen möchten, können wir die Instanziierung von Exception weglassen und stattdessen die Exception-Klasse als Argument übergeben. Diese Änderungen würden zu folgendem Code führen:
Wenn wir es ausführen, erhalten wir das gleiche Ergebnis wie zuvor. Es ist nur kürzer.
quelle