Як я можу написати лямбда-вираз, еквівалентний:
def x():
raise Exception()
Не дозволяється:
y = lambda : raise Exception()
y=lambda...над def y:тоді?
Як я можу написати лямбда-вираз, еквівалентний:
def x():
raise Exception()
Не дозволяється:
y = lambda : raise Exception()
y=lambda...над def y:тоді?
Відповіді:
Існує більше ніж один спосіб виправити Python:
y = lambda: (_ for _ in ()).throw(Exception('foobar'))
Лямбди приймають заяви. Оскільки raise exце заява, ви можете написати підняття загального призначення:
def raise_(ex):
raise ex
y = lambda: raise_(Exception('foobar'))
Але якщо ваша мета - уникнути def, це, очевидно, не вирішує. Однак це дозволяє умовно збільшити винятки, наприклад:
y = lambda x: 2*x if x < 10 else raise_(Exception('foobar'))
Крім того, ви можете створити виняток, не визначаючи названу функцію. Все, що вам потрібно, це міцний шлунок (і 2.x для даного коду):
type(lambda:0)(type((lambda:0).func_code)(
1,1,1,67,'|\0\0\202\1\0',(),(),('x',),'','',1,''),{}
)(Exception())
І міцний шлунковий розчин python3 :
type(lambda: 0)(type((lambda: 0).__code__)(
1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b''),{}
)(Exception())
Спасибі @WarrenSpencer за вказівку дуже проста відповідь , якщо ви не дбаєте , яка збуджується виключення: y = lambda: 1/0.
lambda: 1 / 0. Ви просто закінчите, що ZeroDivisionError буде викинутий замість звичайного винятку. Майте на увазі, що якщо виняток дозволяється пропонувати, може здатися дивним, що хтось налагоджує ваш код, щоб почати бачити купу ZeroDivisionErrors.
y = 1/0це супер розумне рішення, якщо тип
Як щодо:
lambda x: exec('raise(Exception(x))')
SyntaxErrorна Python 2.7.11.
Власне, спосіб є, але це дуже надумано.
Ви можете створити об'єкт коду за допомогою compile()вбудованої функції. Це дозволяє використовувати raiseоператор (або будь-який інший твердження для цього питання), але це викликає ще одне завдання: виконання об'єкта коду. Звичайним способом було б використання execоператора, але це повертає вас до початкової проблеми, а саме, що ви не можете виконувати оператори в lambda(або в eval()цьому випадку).
Рішення - хак. Усі дзвінки, як результат lambdaзаяви, мають атрибут __code__, який насправді можна замінити. Отже, якщо ви створили виклик і замінили його __code__значення об'єктом коду зверху, ви отримаєте те, що можна оцінити без використання операторів. Досягнення всього цього призводить до дуже незрозумілого коду:
map(lambda x, y, z: x.__setattr__(y, z) or x, [lambda: 0], ["__code__"], [compile("raise Exception", "", "single"])[0]()
Сказане робить наступне:
compile()виклик створює об'єкт коду , який викликає виключення;
то lambda: 0повертається відкличний , який не робить нічого , але повертає значення 0 - використовується для виконання вище код об'єкта пізніше;
lambda x, y, zстворює функцію , яка викликає __setattr__метод першого аргументу з іншими аргументами і повертає перший аргумент! Це необхідно, бо __setattr__сама повертається None;
map()виклик приймає результат lambda: 0, і використання lambda x, y, zйого замінює в __code__об'єкт з результатом compile()виклику. Результатом цієї операції з картою є список з одним записом, той, який повернувся lambda x, y, z, і саме тому нам це потрібно lambda: якби ми скористалися __setattr__одразу, ми втратили б посилання на lambda: 0об’єкт!
нарешті, перший (і єдиний) елемент списку, що повертається map()викликом, виконується, в результаті чого викликується об'єкт коду, що в кінцевому підсумку викликає бажане виключення.
Це працює (протестовано в Python 2.6), але це, безумовно, не дуже.
Останнє зауваження: якщо у вас є доступ до typesмодуля (який вимагатиме використання importоператора перед вашим eval), ви можете трохи скоротити цей код: використовуючи types.FunctionType()ви можете створити функцію, яка буде виконувати даний об'єкт коду, щоб ви виграли не потрібен хак для створення функції манекена з lambda: 0заміною значення його __code__атрибута.
Функції, створені за допомогою лямбда-форм, не можуть містити заяви .
Якщо все, що вам потрібно, - лямбда-вираз, який породжує довільне виняток, ви можете виконати це незаконним виразом. Наприклад, lambda x: [][0]спробує отримати доступ до першого елемента в порожньому списку, який підніме IndexError.
УВАГА ! Примітка . Це хак, а не функція. Не використовуйте це в будь-якому (без коду гольфі) коді, який інша людина може бачити чи використовувати.
TypeError: <lambda>() takes exactly 1 positional argument (2 given). Ви впевнені в IndexError?
lambda *x: [][0]. (Первісна версія бере лише один аргумент; для жодних аргументів не використовуйте lambda : [][0]; для двох, використовуйте lambda x,y: [][0]тощо)
lambda x: {}["I want to show this message. Called with: %s" % x] : KeyError: 'I want to show this message. Called with: foo'
Я хотів би дати пояснення ОНОВЛЕННЯ 3 відповіді, наданої Марсело Кантосом:
type(lambda: 0)(type((lambda: 0).__code__)(
1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b''),{}
)(Exception())
lambda: 0є екземпляром builtins.functionкласу.
type(lambda: 0)є builtins.functionклас.
(lambda: 0).__code__є codeоб’єктом. Об'єкт є об'єктом , який містить скомпільований байткод серед інших речей. Він визначений тут у CPython https://github.com/python/cpython/blob/master/Include/code.h . Його методи реалізовані тут https://github.com/python/cpython/blob/master/Objects/codeobject.c . Ми можемо запустити довідку на об'єкт коду:code
Help on code object:
class code(object)
| code(argcount, kwonlyargcount, nlocals, stacksize, flags, codestring,
| constants, names, varnames, filename, name, firstlineno,
| lnotab[, freevars[, cellvars]])
|
| Create a code object. Not for the faint of heart.
type((lambda: 0).__code__)- клас коду.
Так коли ми говоримо
type((lambda: 0).__code__)(
1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b'')
ми викликаємо конструктор об'єкта коду з такими аргументами:
Ви можете прочитати про те, що означають аргументи у визначенні PyCodeObject
https://github.com/python/cpython/blob/master/Include/code.h . Значення 67 для flagsаргументу є, наприклад CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE.
Найважливішим аргументом є той, codestringякий містить інструкції. Подивимося, що вони означають.
>>> import dis
>>> dis.dis(b'|\0\202\1\0')
0 LOAD_FAST 0 (0)
2 RAISE_VARARGS 1
4 <0>
Документацію опкодів можна знайти тут
https://docs.python.org/3.8/library/dis.html#python-bytecode-instructions . Перший байт - це опкод для LOAD_FAST, другий байт - його аргумент, тобто 0.
LOAD_FAST(var_num)
Pushes a reference to the local co_varnames[var_num] onto the stack.
Тож ми натискаємо посилання xна стек. Це varnamesсписок рядків, що містять лише "x". Ми висунемо єдиний аргумент функції, яку ми визначаємо, до стеку.
Наступний байт - це опкод для, RAISE_VARARGSа наступний байт - його аргумент, тобто 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 - це вершина стека. Оскільки ми висунули перший аргумент ( x) нашої функції до стеку і argcдорівнює 1, ми піднімемо
xif, якщо це випадок винятку, або зробимо екземпляр xта піднімемо його інакше.
Останній байт, тобто 0, не використовується. Це не дійсний код коду. Це може бути і не там.
Повертаючись до фрагмента коду, ми будь-що аналізуємо:
type(lambda: 0)(type((lambda: 0).__code__)(
1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b''),{}
)(Exception())
Ми назвали конструктор об'єкта коду:
type((lambda: 0).__code__)(
1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b'')
Передаємо об'єкт коду та порожній словник конструктору функціонального об'єкта:
type(lambda: 0)(type((lambda: 0).__code__)(
1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b''),{}
)
Давайте подзвонимо довідку на об’єкт функції, щоб побачити, що означають аргументи.
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
Потім ми називаємо побудовану функцію, передаючи екземпляр Exception як аргумент. Отже, ми назвали лямбда-функцію, яка викликає виняток. Давайте запустимо фрагмент і побачимо, що він справді працює за призначенням.
>>> 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, in
Exception
Ми бачили, що останній байт байт-коду марний. Давайте не захаращувати цей складний вираз голки. Давайте видалимо цей байт. Крім того, якщо ми хочемо трохи пограти в гольф, ми можемо пропустити інстанцію Exception і замість цього передати клас Exception як аргумент. Ці зміни призведуть до наступного коду:
type(lambda: 0)(type((lambda: 0).__code__)(
1,0,1,1,67,b'|\0\202\1',(),(),('x',),'','',1,b''),{}
)(Exception)
Запустивши його, ми отримаємо такий же результат, як і раніше. Це просто коротше.