Как я могу написать выражение лямбда, которое эквивалентно:
def x():
raise Exception()
Не допускается следующее:
y = lambda : raise Exception()
Как я могу написать выражение лямбда, которое эквивалентно:
def x():
raise Exception()
Не допускается следующее:
y = lambda : raise Exception()
ОБНОВЛЕНИЕ 2: Я был неправ! Оказывается, существует более одного способа скрыть Python:
y = lambda: (_ for _ in ()).throw(Exception('foobar'))
Нет. Lambdas принимает выражения только. 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'))
UPDATE: ОК, поэтому вы можете создать исключение без определения именованной функции. Все, что вам нужно, это сильный желудок (и 2.x для данного кода):
type(lambda:0)(type((lambda:0).func_code)(
1,1,1,67,'|\0\0\202\1\0',(),(),('x',),'','',1,''),{}
)(Exception())
ОБНОВЛЕНИЕ 3: И сильное решение для желудка python3:
type(lambda: 0)(type((lambda: 0).__code__)(
1,0,1,1,67,b'|\0\202\1\0',(),(),('x',),'','',1,b''),{}
)(Exception())
ОБНОВЛЕНИЕ 4: Спасибо @WarrenSpencer за то, что вы указали очень простой ответ, если вам не важно, какое исключение создано: y = lambda: 1/0
.
Как насчет:
lambda x: exec('raise(Exception(x))')
Собственно, есть способ, но он очень надуман.
Вы можете создать объект кода с помощью встроенной функции compile()
. Это позволяет использовать оператор raise
(или любой другой оператор, если на то пошло), но это вызывает еще одну проблему: выполнение объекта кода. Обычным способом было бы использовать оператор exec
, но это вернет вас к исходной проблеме, а именно, что вы не можете выполнять инструкции в lambda
(или eval()
, если на то пошло).
Решение - это взлом. Callables, подобные результату оператора 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.
ОБРАТИТЕ ВНИМАНИЕ: это взломать, а не функцию. Не использовать, это любой (не кодовый гольф-код), который может видеть или использовать другой человек.