Определите лямбда-выражение, которое вызывает исключение - PullRequest
108 голосов
/ 28 ноября 2011

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

def x():
    raise Exception()

Следующее запрещено:

y = lambda : raise Exception()

Ответы [ 5 ]

131 голосов
/ 28 ноября 2011

ОБНОВЛЕНИЕ 2: Я был не прав!Оказывается, существует несколько способов создания оболочки для 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())

ОБНОВЛЕНИЕ 3: И питон3 сильный желудок решение:

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.

43 голосов
/ 03 марта 2012

Как насчет:

lambda x: exec('raise(Exception(x))')
15 голосов
/ 25 октября 2012

На самом деле, есть способ, но он очень надуманный.

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

13 голосов
/ 28 ноября 2012

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

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

13 голосов
/ 28 ноября 2011

Функции, созданные с помощью лямбда-форм , не могут содержать операторов .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...