Определить выражение лямбда, которое вызывает исключение

Как я могу написать выражение лямбда, которое эквивалентно:

def x():
    raise Exception()

Не допускается следующее:

y = lambda : raise Exception()

Ответ 1

ОБНОВЛЕНИЕ 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.

Ответ 2

Как насчет:

lambda x: exec('raise(Exception(x))')

Ответ 3

Собственно, есть способ, но он очень надуман.

Вы можете создать объект кода с помощью встроенной функции 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__.

Ответ 5

Если все, что вы хотите, является выражением лямбда, которое вызывает произвольное исключение, вы можете выполнить это с помощью незаконного выражения. Например, lambda x: [][0] попытается получить доступ к первому элементу в пустом списке, что вызовет индекс IndexError.

ОБРАТИТЕ ВНИМАНИЕ: это взломать, а не функцию. Не использовать, это любой (не кодовый гольф-код), который может видеть или использовать другой человек.