Так что ты не можешь этого сделать. Используйте нормальные функции.
DrTyrsa
1
Какой смысл давать имя анонимной функции?
Джон Ла Рой
2
@gnibbler Вы можете использовать имя для ссылки на функцию. y () проще в использовании, чем (lambda: 0) () в REPL.
Томас Юнг
Так в чем же преимущество по y=lambda...сравнению с этим def y:?
Джон Ла Рой
@gnibbler Некоторый контекст: я хотел определить функцию def g (f, e), которая вызывает f в счастливом случае и e, если обнаружена ошибка. В зависимости от сценария e может вызвать исключение или вернуть какое-либо действительное значение. Чтобы использовать g, я хотел написать g (лямбда x: x * 2, лямбда e: рейз e) или g (лямбда x: x * 2, лямбда e: 0).
Томас Юнг
Ответы:
169
Существует несколько способов создания оболочки для 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 <10else raise_(Exception('foobar'))
В качестве альтернативы вы можете вызвать исключение без определения именованной функции. Все, что вам нужно, это сильный желудок (и 2.x для данного кода):
Если вы не волнует , что тип исключения брошено также следующие работы: lambda: 1 / 0. Вы просто получите ZeroDivisionError вместо обычного исключения. Имейте в виду, что если исключение разрешено распространять, то для отладчика вашего кода может показаться странным, что он начинает видеть кучу ошибок ZeroDivisionErrors.
Уоррен Спенсер
Отличное решение @WarrenSpencer. Большая часть кода не имеет много ошибок с нулевым делением, поэтому она настолько уникальна, как если бы вы могли выбрать тип самостоятельно.
jwg
2
y = 1/0это супер-умное решение, если тип исключения не имеет значения
Сахер Ахвал
3
Кто-нибудь может рассказать нам о том, что на самом деле происходит в решениях «темное искусство / сильный желудок»?
Это довольно забавно, но для написания тестов, где вы хотите смоделировать функции, это работает аккуратно !!!
Каннан Эканат
8
Работает, но вы не должны этого делать.
Авгурар
1
Это не работает для меня, я получаю SyntaxErrorна Python 2.7.11.
Ник Свитинг
Я также получаю вышеупомянутую ошибку (SyntaxError) на Python 2.7.5
Динеш
1
это специфично для Python 3, однако я не думаю, что Python 2 позволяет это.
Сахер Ахвал
16
На самом деле, есть способ, но он очень надуманный.
Вы можете создать объект кода с помощью 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 - используется для выполнения выше код объекта позже;
the 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?
Йовик
4
Ага. Возможно, вы указали неверное количество аргументов? Если вам нужна лямбда-функция, которая может принимать любое количество аргументов, используйте lambda *x: [][0]. (Оригинальная версия принимает только один аргумент; без аргументов - используйте lambda : [][0]; для двух - используйте lambda x,y: [][0]; и т. Д.)
Кайл Стрэнд,
3
Я немного расширил это: lambda x: {}["I want to show this message. Called with: %s" % x] Производит: KeyError: 'I want to show this message. Called with: foo'
ErlVolton
@ErlVolton Умный! Хотя использование этого в любом месте, кроме как в одноразовом сценарии, кажется ужасной идеей ...
Кайл Стрэнд,
Я временно использую в модульных тестах для проекта, где я не удосужился сделать настоящий макет моего регистратора. Это поднимается, если вы пытаетесь записать ошибку или критическое. Так что ... Да, ужасно, хотя и по обоюдному согласию :)
ErlVolton
10
Я хотел бы дать ОБНОВЛЕНИЕ 3 ответа, предоставленного Марсело Кантос:
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) нашей функции в стек и равны argc1, мы будем увеличивать
x если это экземпляр исключения, или делать его, xи поднимать его в противном случае.
Последний байт, т. Е. 0, не используется. Это не действительный код операции. С таким же успехом может и не быть.
Давайте вызовем справку для объекта функции, чтобы увидеть, что означают аргументы.
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,inException
улучшения
Мы видели, что последний байт байт-кода бесполезен. Давайте не будем загромождать это сложное выражение иголками. Давайте удалим этот байт. Также, если мы хотим немного поиграть в гольф, мы можем опустить создание Exception и вместо этого передать класс Exception в качестве аргумента. Эти изменения приведут к следующему коду:
y=lambda...
сравнению с этимdef y:
?Ответы:
Существует несколько способов создания оболочки для Python:
Лямбды принимают заявления. Поскольку
raise ex
это утверждение, вы можете написать универсальный рейзер:Но если ваша цель состоит в том, чтобы избежать
def
, это, очевидно, не сокращает это. Это, однако, позволяет условно вызывать исключения, например:В качестве альтернативы вы можете вызвать исключение без определения именованной функции. Все, что вам нужно, это сильный желудок (и 2.x для данного кода):
И сильный желудочный раствор python3 :
Спасибо @WarrenSpencer за указание очень простой ответ , если вы не заботитесь , которая возбуждается исключение:
y = lambda: 1/0
.источник
lambda: 1 / 0
. Вы просто получите ZeroDivisionError вместо обычного исключения. Имейте в виду, что если исключение разрешено распространять, то для отладчика вашего кода может показаться странным, что он начинает видеть кучу ошибок ZeroDivisionErrors.y = 1/0
это супер-умное решение, если тип исключения не имеет значенияКак насчет:
источник
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 - используется для выполнения выше код объекта позже;the
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 ответа, предоставленного Марсело Кантос:
объяснение
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
type((lambda: 0).__code__)
это класс кодаПоэтому, когда мы говорим,
мы вызываем конструктор объекта кода со следующими аргументами:
Вы можете прочитать о том, что значат аргументы, в определении
PyCodeObject
https://github.com/python/cpython/blob/master/Include/code.h .flags
Например, значение 67 для аргументаCO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE
.Наиболее важным аргументом является аргумент,
codestring
который содержит коды операций. Посмотрим, что они значат.Документацию по кодам операций можно найти здесь https://docs.python.org/3.8/library/dis.html#python-bytecode-instructions . Первый байт - это код операции
LOAD_FAST
, второй байт - его аргумент, т. Е. 0.Поэтому мы помещаем ссылку
x
в стек. Этоvarnames
список строк, содержащих только 'x'. Мы поместим единственный аргумент функции, которую мы определяем, в стек.Следующий байт - это код операции,
RAISE_VARARGS
а следующий байт - его аргумент, т. Е. 1.TOS является вершиной стека. Так как мы поместили первый аргумент (
x
) нашей функции в стек и равныargc
1, мы будем увеличиватьx
если это экземпляр исключения, или делать его,x
и поднимать его в противном случае.Последний байт, т. Е. 0, не используется. Это не действительный код операции. С таким же успехом может и не быть.
Возвращаясь к фрагменту кода, мы в любом случае:
Мы вызвали конструктор объекта кода:
Мы передаем объект кода и пустой словарь в конструктор объекта функции:
Давайте вызовем справку для объекта функции, чтобы увидеть, что означают аргументы.
Затем мы вызываем созданную функцию, передавая экземпляр Exception в качестве аргумента. Следовательно, мы вызвали лямбда-функцию, которая вызывает исключение. Давайте запустим фрагмент и посмотрим, что он действительно работает как задумано.
улучшения
Мы видели, что последний байт байт-кода бесполезен. Давайте не будем загромождать это сложное выражение иголками. Давайте удалим этот байт. Также, если мы хотим немного поиграть в гольф, мы можем опустить создание Exception и вместо этого передать класс Exception в качестве аргумента. Эти изменения приведут к следующему коду:
Когда мы запустим его, мы получим тот же результат, что и раньше. Это просто короче.
источник