Więc nie możesz tego zrobić. Używaj normalnych funkcji.
DrTyrsa
1
Jaki jest sens nadawania nazwy anonimowej funkcji?
John La Rooy
2
@gnibbler Możesz użyć nazwy w celu odniesienia się do funkcji. y () jest łatwiejsze w użyciu niż (lambda: 0) () w REPL.
Thomas Jung
Więc jaka jest przewaga y=lambda...nad def y:tym?
John La Rooy
@gnibbler Pewien kontekst: Chciałem zdefiniować funkcję def g (f, e), która wywołuje f w szczęśliwym przypadku ie, jeśli zostanie wykryty błąd. W zależności od scenariusza e może zgłosić wyjątek lub zwrócić jakąś prawidłową wartość. Aby użyć g, chciałem napisać g (lambda x: x * 2, lambda e: podbić e) lub alternatywnie g (lambda x: x * 2, lambda e: 0).
Thomas Jung
Odpowiedzi:
169
Istnieje więcej niż jeden sposób na skórowanie Pythona:
y =lambda:(_ for _ in()).throw(Exception('foobar'))
Lambdy przyjmują oświadczenia. Ponieważ raise exjest to stwierdzenie, możesz napisać podniesienie ogólnego przeznaczenia:
def raise_(ex):raise ex
y =lambda: raise_(Exception('foobar'))
Ale jeśli Twoim celem jest uniknięcie a def, to oczywiście nie wystarczy. Umożliwia jednak warunkowe zgłaszanie wyjątków, np .:
y =lambda x:2*x if x <10else raise_(Exception('foobar'))
Alternatywnie możesz zgłosić wyjątek bez definiowania nazwanej funkcji. Wystarczy mocny żołądek (i 2.x dla podanego kodu):
Jeśli nie obchodzi, jakiego rodzaju jest wyjątek, następujące prace również: lambda: 1 / 0. Po prostu skończysz z wyrzuceniem ZeroDivisionError zamiast zwykłego wyjątku. Należy pamiętać, że jeśli wyjątek może się rozprzestrzeniać, ktoś debugujący kod może wyglądać dziwnie, aby zobaczyć kilka błędów ZeroDivisionErrors.
Warren Spencer
Świetne rozwiązanie @WarrenSpencer. Większość kodu nie ma wielu błędów dzielenia zerowego, więc jest tak charakterystyczny, jakbyś mógł sam wybrać typ.
jwg,
2
y = 1/0jest super inteligentnym rozwiązaniem, jeśli typ wyjątku jest nieistotny
Saher Ahwal
3
Czy ktoś może nas opowiedzieć, co tak naprawdę dzieje się w rozwiązaniach „mrocznej sztuki / silnego żołądka”?
Jest to dość hakerskie, ale w przypadku pisania testów, w których chcesz mockować funkcje, działa to dobrze !!!
Kannan Ekanath
8
Działa, ale nie powinieneś tego robić.
augurar
1
To nie działa dla mnie, dostaję SyntaxErrorna Python 2.7.11.
Nick Sweeting
Otrzymuję również powyższy błąd (SyntaxError) w Pythonie 2.7.5
Dinesh
1
jest to specyficzne dla Pythona 3, ale nie sądzę, że Python 2 na to pozwala.
Saher Ahwal
16
Właściwie jest sposób, ale jest bardzo wymyślony.
Możesz utworzyć obiekt kodu za pomocą funkcji compile()wbudowanej. Pozwala to na użycie raiseinstrukcji (lub dowolnej innej instrukcji), ale rodzi inne wyzwanie: wykonanie obiektu kodu. Zwykłym sposobem byłoby użycie execinstrukcji, ale to prowadzi cię z powrotem do pierwotnego problemu, a mianowicie, że nie możesz wykonać instrukcji w a lambda(lub eval(), jeśli o to chodzi).
Rozwiązaniem jest włamanie. Wszystkie wywołania, takie jak wynik lambdainstrukcji, mają atrybut __code__, który w rzeczywistości można zastąpić. Tak więc, jeśli utworzysz wywoływalny i zastąpisz jego __code__wartość obiektem kodu z góry, otrzymasz coś, co można ocenić bez użycia instrukcji. Jednak osiągnięcie tego wszystkiego skutkuje bardzo niejasnym kodem:
map(lambda x, y, z: x.__setattr__(y, z) or x, [lambda: 0], ["__code__"], [compile("raise Exception", "", "single"])[0]()
Powyższe wykonuje następujące czynności:
compile()wezwanie tworzy obiekt kodu, który podnosi wyjątek;
gdy lambda: 0wraca wywoływalnym że nic nie robi, ale zwraca wartość 0 - to jest wykorzystywane do wykonywania powyższego celu kodu później;
lambda x, y, ztworzy funkcję, która wywołuje __setattr__metodę pierwszego argumentu z pozostałych argumentów i zwraca pierwszy argument! Jest to konieczne, ponieważ __setattr__samo wraca None;
map()rozmowa odbywa wynik lambda: 0, a przy użyciu lambda x, y, zzastępuje go za __code__obiekt o wyniku compile()rozmowy. Rezultatem tej operacji na mapie jest lista z jednym wpisem, tym zwróconym przez lambda x, y, z, dlatego potrzebujemy tego lambda: gdybyśmy użyli __setattr__od razu, stracilibyśmy odniesienie do lambda: 0obiektu!
na koniec wykonywany jest pierwszy (i jedyny) element listy zwracany przez map()wywołanie, co powoduje wywołanie obiektu kodu, ostatecznie wywołując żądany wyjątek.
Działa (przetestowany w Pythonie 2.6), ale zdecydowanie nie jest ładny.
Ostatnia uwaga: jeśli masz dostęp do typesmodułu (co wymagałoby użycia importinstrukcji przed Twoim eval), to możesz nieco skrócić ten kod: używając types.FunctionType()możesz stworzyć funkcję, która wykona dany obiekt kodu, więc wygrałeś Nie potrzebuję hackowania tworzenia funkcji fikcyjnej zi lambda: 0zastępowania wartości jej __code__atrybutu.
To prawda, ale tak naprawdę nie odpowiada na pytanie; podniesienie wyjątku od wyrażenia lambda jest łatwe do zrobienia (a przechwycenie go może czasami zostać osiągnięte przy użyciu bardzo wymyślnych sztuczek, patrz stackoverflow.com/a/50916686/2560053 lub stackoverflow.com/a/50961836/2560053 ).
Thomas Baruchel
15
Jeśli wszystko, czego chcesz, to wyrażenie lambda, które wywołuje dowolny wyjątek, możesz to zrobić za pomocą niedozwolonego wyrażenia. Na przykład lambda x: [][0]spróbuje uzyskać dostęp do pierwszego elementu z pustej listy, co spowoduje zgłoszenie błędu IndexError.
UWAGA : To hack, a nie funkcja. Nie używaj tego w żadnym (innym niż code-golf) kodzie, który może zobaczyć lub użyć inny człowiek.
W moim przypadku otrzymujemy: TypeError: <lambda>() takes exactly 1 positional argument (2 given). Czy na pewno wystąpił błąd IndexError?
Jovik,
4
Tak. Czy może podałeś niewłaściwą liczbę argumentów? Jeśli potrzebujesz funkcji lambda, która może przyjmować dowolną liczbę argumentów, użyj lambda *x: [][0]. (Oryginalna wersja przyjmuje tylko jeden argument; bez argumentów użyj lambda : [][0]; dla dwóch użyj lambda x,y: [][0]; itp.)
Kyle Strand
3
Trochę to rozszerzyłem: lambda x: {}["I want to show this message. Called with: %s" % x] Produkuje: KeyError: 'I want to show this message. Called with: foo'
ErlVolton,
@ErlVolton Clever! Chociaż używanie tego wszędzie, z wyjątkiem jednorazowego skryptu, wydaje się okropnym pomysłem ...
Kyle Strand
Tymczasowo używam testów jednostkowych dla projektu, w którym nie zadałem sobie trudu, aby zrobić prawdziwą próbę mojego rejestratora. Podnosi się, jeśli spróbujesz zarejestrować błąd lub krytyczny. A więc ... Tak okropne, chociaż za zgodą :)
ErlVolton
10
Chciałbym wyjaśnić AKTUALIZACJĘ 3 odpowiedzi udzielonej przez Marcelo Cantos:
LOAD_FAST(var_num)Pushes a reference to the local co_varnames[var_num] onto the stack.
Więc umieszczamy odniesienie do xna stosie. varnamesJest listą ciągów zawierających tylko „x”. Jedyny argument funkcji, którą definiujemy, wrzucimy na stos.
Następny bajt to kod operacji dla, RAISE_VARARGSa następny bajt to jego argument, tj. 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)
TOS jest szczytem stosu. Ponieważ umieściliśmy pierwszy argument ( x) naszej funkcji na stosie i argcwynosi 1, podniesiemy wartość,
xjeśli jest to wyjątek, lub utworzymy instancję xi podniesiemy ją w przeciwnym razie.
Ostatni bajt, czyli 0, nie jest używany. To nie jest prawidłowy kod operacji. Równie dobrze może go tam nie być.
Wracając do fragmentu kodu, który teraz analizujemy:
Wywołajmy pomoc dotyczącą obiektu funkcji, aby zobaczyć, co oznaczają argumenty.
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
Następnie wywołujemy skonstruowaną funkcję, przekazując jako argument wystąpienie wyjątku. W konsekwencji nazwaliśmy funkcję lambda, która zgłasza wyjątek. Uruchommy fragment i przekonajmy się, że rzeczywiście działa zgodnie z przeznaczeniem.
>>> 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
Improvements
Widzieliśmy, że ostatni bajt kodu bajtowego jest bezużyteczny. Nie zaśmiecajmy tej skomplikowanej ekspresji igliwie. Usuńmy ten bajt. Również jeśli chcemy trochę pograć w golfa, możemy pominąć tworzenie wystąpienia Exception i zamiast tego przekazać klasę Exception jako argument. Te zmiany spowodowałyby następujący kod:
y=lambda...
naddef y:
tym?Odpowiedzi:
Istnieje więcej niż jeden sposób na skórowanie Pythona:
Lambdy przyjmują oświadczenia. Ponieważ
raise ex
jest to stwierdzenie, możesz napisać podniesienie ogólnego przeznaczenia:Ale jeśli Twoim celem jest uniknięcie a
def
, to oczywiście nie wystarczy. Umożliwia jednak warunkowe zgłaszanie wyjątków, np .:Alternatywnie możesz zgłosić wyjątek bez definiowania nazwanej funkcji. Wystarczy mocny żołądek (i 2.x dla podanego kodu):
I mocne rozwiązanie żołądkowe Python3 :
Dzięki @WarrenSpencer za wskazanie bardzo prostą odpowiedź, jeśli nie obchodzi który jest wyjątek:
y = lambda: 1/0
.źródło
lambda: 1 / 0
. Po prostu skończysz z wyrzuceniem ZeroDivisionError zamiast zwykłego wyjątku. Należy pamiętać, że jeśli wyjątek może się rozprzestrzeniać, ktoś debugujący kod może wyglądać dziwnie, aby zobaczyć kilka błędów ZeroDivisionErrors.y = 1/0
jest super inteligentnym rozwiązaniem, jeśli typ wyjątku jest nieistotnyCo powiesz na:
źródło
SyntaxError
na Python 2.7.11.Właściwie jest sposób, ale jest bardzo wymyślony.
Możesz utworzyć obiekt kodu za pomocą funkcji
compile()
wbudowanej. Pozwala to na użycieraise
instrukcji (lub dowolnej innej instrukcji), ale rodzi inne wyzwanie: wykonanie obiektu kodu. Zwykłym sposobem byłoby użycieexec
instrukcji, ale to prowadzi cię z powrotem do pierwotnego problemu, a mianowicie, że nie możesz wykonać instrukcji w alambda
(lubeval()
, jeśli o to chodzi).Rozwiązaniem jest włamanie. Wszystkie wywołania, takie jak wynik
lambda
instrukcji, mają atrybut__code__
, który w rzeczywistości można zastąpić. Tak więc, jeśli utworzysz wywoływalny i zastąpisz jego__code__
wartość obiektem kodu z góry, otrzymasz coś, co można ocenić bez użycia instrukcji. Jednak osiągnięcie tego wszystkiego skutkuje bardzo niejasnym kodem:map(lambda x, y, z: x.__setattr__(y, z) or x, [lambda: 0], ["__code__"], [compile("raise Exception", "", "single"])[0]()
Powyższe wykonuje następujące czynności:
compile()
wezwanie tworzy obiekt kodu, który podnosi wyjątek;gdy
lambda: 0
wraca wywoływalnym że nic nie robi, ale zwraca wartość 0 - to jest wykorzystywane do wykonywania powyższego celu kodu później;lambda x, y, z
tworzy funkcję, która wywołuje__setattr__
metodę pierwszego argumentu z pozostałych argumentów i zwraca pierwszy argument! Jest to konieczne, ponieważ__setattr__
samo wracaNone
;map()
rozmowa odbywa wyniklambda: 0
, a przy użyciulambda x, y, z
zastępuje go za__code__
obiekt o wynikucompile()
rozmowy. Rezultatem tej operacji na mapie jest lista z jednym wpisem, tym zwróconym przezlambda x, y, z
, dlatego potrzebujemy tegolambda
: gdybyśmy użyli__setattr__
od razu, stracilibyśmy odniesienie dolambda: 0
obiektu!na koniec wykonywany jest pierwszy (i jedyny) element listy zwracany przez
map()
wywołanie, co powoduje wywołanie obiektu kodu, ostatecznie wywołując żądany wyjątek.Działa (przetestowany w Pythonie 2.6), ale zdecydowanie nie jest ładny.
Ostatnia uwaga: jeśli masz dostęp do
types
modułu (co wymagałoby użyciaimport
instrukcji przed Twoimeval
), to możesz nieco skrócić ten kod: używająctypes.FunctionType()
możesz stworzyć funkcję, która wykona dany obiekt kodu, więc wygrałeś Nie potrzebuję hackowania tworzenia funkcji fikcyjnej zilambda: 0
zastępowania wartości jej__code__
atrybutu.źródło
Funkcje utworzone za pomocą formularzy lambda nie mogą zawierać instrukcji .
źródło
Jeśli wszystko, czego chcesz, to wyrażenie lambda, które wywołuje dowolny wyjątek, możesz to zrobić za pomocą niedozwolonego wyrażenia. Na przykład
lambda x: [][0]
spróbuje uzyskać dostęp do pierwszego elementu z pustej listy, co spowoduje zgłoszenie błędu IndexError.UWAGA : To hack, a nie funkcja. Nie używaj tego w żadnym (innym niż code-golf) kodzie, który może zobaczyć lub użyć inny człowiek.
źródło
TypeError: <lambda>() takes exactly 1 positional argument (2 given)
. Czy na pewno wystąpił błąd IndexError?lambda *x: [][0]
. (Oryginalna wersja przyjmuje tylko jeden argument; bez argumentów użyjlambda : [][0]
; dla dwóch użyjlambda x,y: [][0]
; itp.)lambda x: {}["I want to show this message. Called with: %s" % x]
Produkuje:KeyError: 'I want to show this message. Called with: foo'
Chciałbym wyjaśnić AKTUALIZACJĘ 3 odpowiedzi udzielonej przez Marcelo Cantos:
Wyjaśnienie
lambda: 0
jest instancjąbuiltins.function
klasy.type(lambda: 0)
jestbuiltins.function
klasą.(lambda: 0).__code__
jestcode
przedmiotem. Obiekt jest obiektem, który posiada skompilowany kod bajtowy między innymi. Jest zdefiniowany tutaj w CPython https://github.com/python/cpython/blob/master/Include/code.h . Jego metody są zaimplementowane tutaj https://github.com/python/cpython/blob/master/Objects/codeobject.c . Możemy uruchomić pomoc dotyczącą obiektu kodu:code
type((lambda: 0).__code__)
to klasa kodu.Więc kiedy mówimy
wywołujemy konstruktora obiektu kodu z następującymi argumentami:
O tym, co oznaczają argumenty, możesz przeczytać w definicji
PyCodeObject
https://github.com/python/cpython/blob/master/Include/code.h . Na przykład wartość 67flags
argumentu toCO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE
.Najważniejszym argumentem jest ten,
codestring
który zawiera kody instrukcji. Zobaczmy, co mają na myśli.Dokumentację kodów operacyjnych można znaleźć tutaj https://docs.python.org/3.8/library/dis.html#python-bytecode-instructions . Pierwszy bajt to kod
LOAD_FAST
operacji, drugi bajt to jego argument, tj. 0.Więc umieszczamy odniesienie do
x
na stosie.varnames
Jest listą ciągów zawierających tylko „x”. Jedyny argument funkcji, którą definiujemy, wrzucimy na stos.Następny bajt to kod operacji dla,
RAISE_VARARGS
a następny bajt to jego argument, tj. 1.TOS jest szczytem stosu. Ponieważ umieściliśmy pierwszy argument (
x
) naszej funkcji na stosie iargc
wynosi 1, podniesiemy wartość,x
jeśli jest to wyjątek, lub utworzymy instancjęx
i podniesiemy ją w przeciwnym razie.Ostatni bajt, czyli 0, nie jest używany. To nie jest prawidłowy kod operacji. Równie dobrze może go tam nie być.
Wracając do fragmentu kodu, który teraz analizujemy:
Nazwaliśmy konstruktora obiektu kodu:
Przekazujemy obiekt kodu i pusty słownik do konstruktora obiektu funkcji:
Wywołajmy pomoc dotyczącą obiektu funkcji, aby zobaczyć, co oznaczają argumenty.
Następnie wywołujemy skonstruowaną funkcję, przekazując jako argument wystąpienie wyjątku. W konsekwencji nazwaliśmy funkcję lambda, która zgłasza wyjątek. Uruchommy fragment i przekonajmy się, że rzeczywiście działa zgodnie z przeznaczeniem.
Improvements
Widzieliśmy, że ostatni bajt kodu bajtowego jest bezużyteczny. Nie zaśmiecajmy tej skomplikowanej ekspresji igliwie. Usuńmy ten bajt. Również jeśli chcemy trochę pograć w golfa, możemy pominąć tworzenie wystąpienia Exception i zamiast tego przekazać klasę Exception jako argument. Te zmiany spowodowałyby następujący kod:
Kiedy go uruchomimy, uzyskamy taki sam wynik jak poprzednio. Jest po prostu krótszy.
źródło